From 80ae16bfea2240a3d2fc774ab261703c038723ce Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Fri, 15 Nov 2013 12:39:24 -0600 Subject: [PATCH 1/6] Extract keyfile line assembly into private methods --- lib/github/auth/keys_file.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/github/auth/keys_file.rb b/lib/github/auth/keys_file.rb index a450468..bbb4883 100644 --- a/lib/github/auth/keys_file.rb +++ b/lib/github/auth/keys_file.rb @@ -20,7 +20,7 @@ def write!(keys) unless keys_file_content.empty? || keys_file_content.end_with?("\n") keys_file.write "\n" end - keys_file.write "#{"command=\"#{command}\" " if command}#{key}\n" + keys_file.write key_line(key) end end end @@ -69,5 +69,18 @@ def keys_file_content_without(keys) content << "\n" unless content.empty? || content.end_with?("\n") end end + + def key_line_prefixes + prefixes = [] + prefixes << %Q{command="#{command}"} if command + prefixes.join(',') + end + + def key_line(key) + line = [] + line << key_line_prefixes unless key_line_prefixes.empty? + line << "#{key}\n" + line.join(' ') + end end end From 1b16eb4f72e9d042b09312f5620f258acfe0c8fe Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Fri, 15 Nov 2013 12:40:09 -0600 Subject: [PATCH 2/6] KeysFile takes a "ssh_options" option to add options to key file --- lib/github/auth/keys_file.rb | 6 ++++-- spec/unit/github/auth/keys_file_spec.rb | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/github/auth/keys_file.rb b/lib/github/auth/keys_file.rb index bbb4883..57d7146 100644 --- a/lib/github/auth/keys_file.rb +++ b/lib/github/auth/keys_file.rb @@ -1,7 +1,7 @@ module Github::Auth # Write and delete keys from the authorized_keys file class KeysFile - attr_reader :path, :command + attr_reader :path, :command, :ssh_options PermissionDeniedError = Class.new StandardError FileDoesNotExistError = Class.new StandardError @@ -10,7 +10,8 @@ class KeysFile def initialize(options = {}) @path = File.expand_path(options[:path] || DEFAULT_PATH) - @command = options[:command] + @command = options[:command] + @ssh_options = Array(options[:ssh_options]) end def write!(keys) @@ -72,6 +73,7 @@ def keys_file_content_without(keys) def key_line_prefixes prefixes = [] + prefixes << ssh_options.join(',') unless ssh_options.empty? prefixes << %Q{command="#{command}"} if command prefixes.join(',') end diff --git a/spec/unit/github/auth/keys_file_spec.rb b/spec/unit/github/auth/keys_file_spec.rb index c6d046c..afb3045 100644 --- a/spec/unit/github/auth/keys_file_spec.rb +++ b/spec/unit/github/auth/keys_file_spec.rb @@ -92,6 +92,19 @@ end end + context 'with ssh options' do + let(:options) {{ path: path, ssh_options: ['no-way', 'no-how'] }} + let(:keys) {[ Github::Auth::Key.new('chris', 'abc123') ]} + + it_should_behave_like 'a successful key addition' + + it 'prefixes the key with the comma-separated ssh options' do + subject.write! keys + + expect(keys_file.read).to include 'no-way,no-how' + end + end + context 'with existing keys in the keys file' do let(:existing_keys) { %w(abc123 def456 ghi789) } let(:keys) {[ Github::Auth::Key.new('chris', 'jkl012') ]} From beb17b4a9242ec12a392f33ee4eaa72b156e7bcb Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Fri, 15 Nov 2013 12:40:32 -0600 Subject: [PATCH 3/6] Add --ssh-options option to cli --- lib/github/auth/cli.rb | 8 +++++++- spec/acceptance/github/auth/cli_spec.rb | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/github/auth/cli.rb b/lib/github/auth/cli.rb index c0a1cca..4523631 100644 --- a/lib/github/auth/cli.rb +++ b/lib/github/auth/cli.rb @@ -8,6 +8,7 @@ class CLI < Thor option :users, type: :array, required: true option :command, type: :string + option :ssh_options, type: :array desc 'add', 'Add GitHub users to authorized keys' long_desc <<-LONGDESC `gh-auth add` is used to add one or more GitHub user's public SSH keys @@ -22,11 +23,16 @@ class CLI < Thor the `--command` option. > $ gh-auth add --users=chrishunt --command="tmux attach" + + You may also provide options for the key entry, such as preventing + port forwarding. To do this, include the `--ssh-options` option. + + > $ gh-auth add --users=chrishunt --ssh-options=no-port-forwarding LONGDESC def add on_keys_file :write!, "Adding #{keys.count} key(s) to '#{keys_file.path}'", - { command: options[:command] } + { command: options[:command], ssh_options: options[:ssh_options] } end option :users, type: :array, required: true diff --git a/spec/acceptance/github/auth/cli_spec.rb b/spec/acceptance/github/auth/cli_spec.rb index 889af5e..59a318c 100644 --- a/spec/acceptance/github/auth/cli_spec.rb +++ b/spec/acceptance/github/auth/cli_spec.rb @@ -54,6 +54,18 @@ def cli(args = []) expect(keys_file.read.strip).to be_empty end + it 'supports ssh options' do + cli %w(add --users=chrishunt + --ssh-options=no-port-forwarding no-agent-forwarding) + + expect(keys_file.read).to include 'no-port-forwarding,no-agent-forwarding' + + keys_file.rewind + cli %w(remove --users=chrishunt) + + expect(keys_file.read.strip).to be_empty + end + it 'prints version information' do output = capture_stdout do cli %w(version) From b0730efc31a4a906aa72327f888dc61d9100e99c Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 19 Nov 2013 21:02:09 -0600 Subject: [PATCH 4/6] KeyFile takes a no_forwarding option to disable all forwarding --- lib/github/auth/keys_file.rb | 10 ++++++++++ spec/unit/github/auth/keys_file_spec.rb | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/lib/github/auth/keys_file.rb b/lib/github/auth/keys_file.rb index 57d7146..fc10309 100644 --- a/lib/github/auth/keys_file.rb +++ b/lib/github/auth/keys_file.rb @@ -1,6 +1,12 @@ module Github::Auth # Write and delete keys from the authorized_keys file class KeysFile + NO_FORWARDING_OPTIONS = %w( + no-port-forwarding + no-X11-forwarding + no-agent-forwarding + ) + attr_reader :path, :command, :ssh_options PermissionDeniedError = Class.new StandardError @@ -12,6 +18,10 @@ def initialize(options = {}) @path = File.expand_path(options[:path] || DEFAULT_PATH) @command = options[:command] @ssh_options = Array(options[:ssh_options]) + + if options[:no_forwarding] + @ssh_options |= NO_FORWARDING_OPTIONS + end end def write!(keys) diff --git a/spec/unit/github/auth/keys_file_spec.rb b/spec/unit/github/auth/keys_file_spec.rb index afb3045..a71aea0 100644 --- a/spec/unit/github/auth/keys_file_spec.rb +++ b/spec/unit/github/auth/keys_file_spec.rb @@ -105,6 +105,22 @@ end end + context 'with forwarding disabled' do + let(:options) {{ path: path, no_forwarding: true }} + let(:keys) {[ Github::Auth::Key.new('chris', 'abc123') ]} + + it_should_behave_like 'a successful key addition' + + it 'prefixes the key with all the options to disable forwarding' do + comma_separated_no_forwarding_options = + Github::Auth::KeysFile::NO_FORWARDING_OPTIONS.join(',') + + subject.write! keys + + expect(keys_file.read).to include comma_separated_no_forwarding_options + end + end + context 'with existing keys in the keys file' do let(:existing_keys) { %w(abc123 def456 ghi789) } let(:keys) {[ Github::Auth::Key.new('chris', 'jkl012') ]} From b780f7537722416eec78629e318688a8fb39fda5 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Tue, 19 Nov 2013 21:02:46 -0600 Subject: [PATCH 5/6] CLI accepts the --no-forwarding option --- lib/github/auth/cli.rb | 10 +++++++++- spec/acceptance/github/auth/cli_spec.rb | 13 +++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/github/auth/cli.rb b/lib/github/auth/cli.rb index 4523631..b19224e 100644 --- a/lib/github/auth/cli.rb +++ b/lib/github/auth/cli.rb @@ -9,6 +9,7 @@ class CLI < Thor option :users, type: :array, required: true option :command, type: :string option :ssh_options, type: :array + option :no_forwarding, type: :boolean desc 'add', 'Add GitHub users to authorized keys' long_desc <<-LONGDESC `gh-auth add` is used to add one or more GitHub user's public SSH keys @@ -28,11 +29,18 @@ class CLI < Thor port forwarding. To do this, include the `--ssh-options` option. > $ gh-auth add --users=chrishunt --ssh-options=no-port-forwarding + + To disable all forwarding for their connection, use the + `--no-forwarding` option. + + > $ gh-auth add --users=chrishunt --no-forwarding LONGDESC def add on_keys_file :write!, "Adding #{keys.count} key(s) to '#{keys_file.path}'", - { command: options[:command], ssh_options: options[:ssh_options] } + { command: options[:command], + ssh_options: options[:ssh_options], + no_forwarding: options[:no_forwarding] } end option :users, type: :array, required: true diff --git a/spec/acceptance/github/auth/cli_spec.rb b/spec/acceptance/github/auth/cli_spec.rb index 59a318c..d64942a 100644 --- a/spec/acceptance/github/auth/cli_spec.rb +++ b/spec/acceptance/github/auth/cli_spec.rb @@ -66,6 +66,19 @@ def cli(args = []) expect(keys_file.read.strip).to be_empty end + it 'supports no-forwarding option' do + comma_separated_no_forwarding_options = + Github::Auth::KeysFile::NO_FORWARDING_OPTIONS.join(',') + + cli %w(add --users=chrishunt --no-forwarding) + + expect(keys_file.read).to include comma_separated_no_forwarding_options + + keys_file.rewind + cli %w(remove --users=chrishunt) + + end + it 'prints version information' do output = capture_stdout do cli %w(version) From 85344c4d2ccf6ccf43c4ed3843aa27710f3ea038 Mon Sep 17 00:00:00 2001 From: Jay Hayes Date: Fri, 15 Nov 2013 12:44:42 -0600 Subject: [PATCH 6/6] Add example of --ssh-options and --no-forwarding options to readme --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index f8de0d6..0c54166 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,24 @@ $ gh-auth add --users=chrishunt --command="tmux attach" Adding 2 key(s) to '/Users/chris/.ssh/authorized_keys' ``` +Allowing someone to SSH to your machine can be a scary thing. Restricting them +to a single command goes a little way to help secure things. If you want to +keep them from forwarding ports, you can use the `--ssh-options` option to +add that to their key entry. + +```bash +$ gh-auth add --users=chrishunt --ssh-options=no-port-forwarding +Adding 2 key(s) to '/Users/chris/.ssh/authorized_keys' +``` + +As a convenience, you may also use the `--no-forwarding` option to prevent all +forwarding (i.e. `no-port-forwarding,no-X11-forwarding,no-agent-forwarding`). + +```bash +$ gh-auth add --users=chrishunt --no-forwarding +Adding 2 key(s) to '/Users/chris/.ssh/authorized_keys' +``` + That was easy! When we're done working, you can revoke my access with: ```bash