vpcjump 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 91bc9c02d6d0df3b88ee4e7660859087db1666ef
4
+ data.tar.gz: 058a24349db38b81fb048eb9a464493c47701559
5
+ SHA512:
6
+ metadata.gz: 269e9d7410ab40a60c21bd3625b948e3cd96ca0e4712109dab73c7debf413a4ed94fcbb0ebeb48332f43567e908d9780bffe87169d5cd5cd6a6b7d8472d16699
7
+ data.tar.gz: 2cf93eb463b09b39e2da84b575e38c7fd016de82089d7b7815d1bc265b670b65023aba0f5f28c54ea234bdc64c407a49801ab1e89e581c55df1bb739c644e4ed
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vpcjump.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # `vpcjump`
2
+
3
+ `vpcjump` is a helper tool to make it as easy as possible to connect to a jumpbox in an AWS [VPC](https://aws.amazon.com/vpc/).
4
+
5
+ What makes `vpcjump` unique is that your jumpbox doesn't need a public IP nor needs to open any ports in a security group. What it _does_ need is outbound Internet connectivity on port 443 - whether via a [NAT instance](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_NAT_Instance.html), Amazon-managed [NAT gateway](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-nat-gateway.html) or some custom solution.
6
+
7
+ ## Setup
8
+
9
+ * Install the tool: `gem install vpcjump`
10
+ * Sign up for [ngrok](https://ngrok.com/) and note down your authentication token
11
+ * Start an instance in EC2 - this only needs to be done once.
12
+
13
+ You can launch the EC2 instance from the AWS web console. Amazon Linux on a t2.nano is sufficient. The t2.nano is less than $5/month (as little as $2.16/month if paid upfront!) so leaving it always up is quite affordable.
14
+
15
+ When on step 3 (**Configure Instance Details**), expand **Advanced Details** and paste the following into **User data**:
16
+
17
+ ```
18
+ #!/bin/bash
19
+ cd /tmp
20
+ curl https://amazon-ssm-ap-southeast-2.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
21
+ yum install -y amazon-ssm-agent.rpm
22
+ curl https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip -o ngrok.zip
23
+ unzip ngrok.zip
24
+ mv ngrok /usr/bin/
25
+ ```
26
+
27
+ You will also need to create and select an **IAM Role** for this instance to grant it permission to use [SSM](http://docs.aws.amazon.com/ssm/latest/APIReference/Welcome.html). To do this, you:
28
+
29
+ * Click **Create new IAM role**
30
+ * Click **Create New Role**
31
+ * Specify a role name of your choice, e.g.`SSMForVpcjump`, then hit next.
32
+ * Under **AWS Service Roles**, select **Amazon EC2 Role for Simple Systems Manager**, then hit next.
33
+ * Tick the checkbox for **AmazonEC2RoleforSSM**, then next.
34
+ * Click **Create Role**.
35
+ * Return to the instance creation screen, hit refresh next to **IAM Role**, then select the role you just created.
36
+
37
+ You can now proceed through the instance creation process - all the default values are fine. You can delete the security group rule opening up access to port 22 if you wish, this is unneeded. Once the instance has launched, note down the instance ID (looks like `i-abcdef`) as you will use it later.
38
+
39
+ ## Usage
40
+
41
+ Invocation can be as simple as `vpcjump --ngrok-token <auth token> --instance-id i-abc123 ssh`. This takes about 30 seconds and will SSH you into the jumpbox instance. The complete list of options is:
42
+
43
+ ```
44
+ $ vpcjump --help
45
+ Usage:
46
+ vpcjump [OPTIONS] SUBCOMMAND [ARG] ...
47
+
48
+ Parameters:
49
+ SUBCOMMAND subcommand
50
+ [ARG] ... subcommand arguments
51
+
52
+ Subcommands:
53
+ kill Terminate ngrok tunnel
54
+ ssh SSH into the jumpbox
55
+
56
+ Options:
57
+ --instance-id INSTANCE ID EC2 instance id of jump box (e.g. i-abc123)
58
+ --name INSTANCE NAME EC2 instance name of jump box
59
+ --ngrok-token NGROK TOKEN ngrok auth token
60
+ --ngrok-region NGROK REGION ngrok region (default: "us")
61
+ -v, --verbose Output AWS API calls
62
+ -h, --help print help
63
+
64
+ $ vpcjump ssh --help
65
+ Usage:
66
+ vpcjump ssh [OPTIONS] [SSHPARAMS] ...
67
+
68
+ Parameters:
69
+ [SSHPARAMS] ... Arguments to pass to SSH
70
+
71
+ Options:
72
+ --ssh-user SSH USER SSH user (default: "ec2-user")
73
+ ```
74
+
75
+ Often you will want to use the jumpbox as an intermediary in order to connect to a second instance. This can be done using SSH port forwarding, e.g. `vpcjump --ngrok-token <auth token> --instance-id i-abc123 ssh -- -L 3389:172.16.0.1:3389` - This will let you to connect to Microsoft Remote Desktop on 127.0.0.1 and it will connect to the remote instance!
76
+
77
+ Finally, you may wish to terminate the jumpbox tunnel early. This can be done as such: `vpcjump --instance-id i-abc123 kill`.
78
+
79
+ ## How it works
80
+
81
+ `vpcjump` uses AWS SSM to remotely execute shell commands on your jumpbox. You can see the precise SSM and SSH commands it executes on your behalf by passing the `--verbose` flag during execution. It does the following:
82
+
83
+ * Use SSM to execute `ngrok tcp 22` with your provided auth token. This creates a tunnel over HTTPS between the instance and ngrok's tunnelling service.
84
+ * Use SSM to query ngrok's local API: `curl -s http://localhost:4040/api/tunnels`. This returns the ngrok hostname and port that are forwarded to port 22 on your jump box.
85
+ * Execute `ssh -p <ngrok port> <ssh user>@<ngrok host>` to log into your jumpbox through the ngrok tunnel.
86
+ * Use SSM to execute `killall ngrok` on the remote instance when you explicitly terminate the tunnel. If you don't do this, the SSM agent process on the remote instance will terminate the ngrok tunnel after a predefined period.
87
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler/gem_tasks'
2
+ task :default => :spec
data/exe/vpcjump ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+ require 'uri'
3
+ require 'json'
4
+ require 'clamp'
5
+ require 'aws-sdk'
6
+ require 'logger'
7
+
8
+ module VpcJump
9
+ class CLI < Clamp::Command
10
+ option '--instance-id', 'INSTANCE ID', 'EC2 instance id of jump box (e.g. i-abc123)'
11
+ option '--name', 'INSTANCE NAME', 'EC2 instance name of jump box'
12
+ option '--ngrok-token', 'NGROK TOKEN', 'ngrok auth token'
13
+ option '--ngrok-region', 'NGROK REGION', 'ngrok region', default: 'us'
14
+ option ['-v', '--verbose'], :flag, 'Output AWS API calls'
15
+
16
+ subcommand 'kill', 'Terminate ngrok tunnel' do
17
+ def execute
18
+ Aws.config[:logger] = Logger.new($stdout) if verbose?
19
+
20
+ id = instance_id
21
+ ssm_exec id, 'killall ngrok'
22
+ end
23
+ end
24
+
25
+ subcommand 'ssh', 'SSH into the jumpbox' do
26
+ option '--ssh-user', 'SSH USER', 'SSH user', default: 'ec2-user'
27
+ parameter '[SSHPARAMS] ...', 'Arguments to pass to SSH'
28
+
29
+ def default_sshparams_list
30
+ []
31
+ end
32
+
33
+ def execute
34
+ Aws.config[:logger] = Logger.new($stdout) if verbose?
35
+
36
+ id = instance_id
37
+
38
+ ssm_exec id, "ngrok tcp --log stdout --region #{ngrok_region} --authtoken #{ngrok_token} 22"
39
+ sleep 20
40
+
41
+ tunnels = []
42
+
43
+ loop do
44
+ resp = ssm_exec id, 'curl -s http://localhost:4040/api/tunnels'
45
+ output = ssm_output resp.command_id
46
+ json = JSON.parse output
47
+ tunnels = json['tunnels']
48
+ break if tunnels.length > 0
49
+ end
50
+
51
+ url = tunnels[0]['public_url']
52
+ uri = URI.parse url
53
+ port = uri.port
54
+ host = uri.hostname
55
+
56
+ cmd = "ssh -p #{port} #{sshparams_list.join(' ')} #{ssh_user}@#{host}"
57
+ puts cmd if verbose?
58
+ exec cmd
59
+ end
60
+ end
61
+
62
+ def ssm_exec(instance_id, command)
63
+ ssm = Aws::SSM::Client.new
64
+
65
+ resp = ssm.send_command(
66
+ instance_ids: [instance_id],
67
+ document_name: 'AWS-RunShellScript',
68
+ parameters: {
69
+ commands: [command]
70
+ }
71
+ )
72
+
73
+ resp.command
74
+ end
75
+
76
+ def ssm_output(command_id)
77
+ ssm = Aws::SSM::Client.new
78
+
79
+ loop do
80
+ sleep 1
81
+ response = ssm.list_command_invocations command_id: command_id, details: true
82
+ invocation = response.command_invocations.first
83
+
84
+ if ['Pending', 'InProgress'].include?(invocation.status)
85
+ next
86
+ end
87
+
88
+ escaped = invocation.command_plugins[0].output
89
+ return escaped.gsub('&quot;', '"')
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ VpcJump::CLI.run
@@ -0,0 +1,3 @@
1
+ module Vpcjump
2
+ VERSION = '0.1.0'
3
+ end
data/lib/vpcjump.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'vpcjump/version'
2
+
3
+ module Vpcjump
4
+ # Your code goes here...
5
+ end
data/vpcjump.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vpcjump/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'vpcjump'
8
+ spec.version = Vpcjump::VERSION
9
+ spec.authors = ['Aidan Steele']
10
+ spec.email = ['aidan.steele@glassechidna.com.au']
11
+ spec.homepage = 'https://github.com/aidansteele/vpcjump'
12
+
13
+ spec.summary = %q{Helper tool for connecting to jumpboxes in AWS.}
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.13'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'pry-rescue'
25
+ spec.add_development_dependency 'pry-stack_explorer'
26
+
27
+ spec.add_dependency 'clamp'
28
+ spec.add_dependency 'aws-sdk', '~> 2'
29
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vpcjump
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Aidan Steele
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-rescue
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-stack_explorer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: clamp
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aws-sdk
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2'
97
+ description:
98
+ email:
99
+ - aidan.steele@glassechidna.com.au
100
+ executables:
101
+ - vpcjump
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - README.md
108
+ - Rakefile
109
+ - exe/vpcjump
110
+ - lib/vpcjump.rb
111
+ - lib/vpcjump/version.rb
112
+ - vpcjump.gemspec
113
+ homepage: https://github.com/aidansteele/vpcjump
114
+ licenses: []
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.4.5.1
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Helper tool for connecting to jumpboxes in AWS.
136
+ test_files: []