vtk 0.5.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fff239de6fb79f245ca21cd8aefe7869594759d3f04094dec4555b45a27781ea
4
- data.tar.gz: 3d5a6d37f2bab2ce2ca1a21ac38bb374a390c8da7c75a71c41659abfd4485f47
3
+ metadata.gz: 5330833427f91a051537c3289baa3dff860f28a03fe3b47508d34e2eaeaffe3d
4
+ data.tar.gz: 00bf91a02ad6e3640c6a70cd1ab58581e70e8b73d04f29c8b8711ae526b3b8a8
5
5
  SHA512:
6
- metadata.gz: c6910620a06690220b58138ba1a3a6fabe9a6cb58129c05a575b64673aea08398341c7cbcf3ced41f63f72423f76e8401fd5bd7f03c2e25e9743f9b7c579767b
7
- data.tar.gz: 2a9bd5ad03d3ab7f8bcd8a6d521816d8a89199d32aed435b009585ffbe80fac68236bdb6a96eea7f9f27a0bb2bd2ab30b4b955cc54f83c475a40e55468928ce8
6
+ metadata.gz: 60b2d9deaa896280656e6ccc25af0d200f020e82bf0a3f2a70244d1b3eed7d0288615049fe4ac1d2153f917475c5b5c48b3f51e9febb945b34bc5325e5ec2e99
7
+ data.tar.gz: e7a97e25802f2e2c16075b5033e74fe1d79a6878fd6bcad748bbf1272c617f4b1878c75e71996b9326c5a7a6ff85c39c4804ac3a8a1df7f884cc00d7839eaab6
data/.rubocop.yml CHANGED
@@ -1,5 +1,8 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
1
3
  AllCops:
2
4
  NewCops: enable
5
+ TargetRubyVersion: 2.5
3
6
 
4
7
  Layout/LineLength:
5
8
  Max: 120
data/.rubocop_todo.yml ADDED
@@ -0,0 +1,12 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2021-08-06 20:18:41 UTC using RuboCop version 1.8.1.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 1
10
+ # Configuration parameters: CountComments, CountAsOne.
11
+ Metrics/ClassLength:
12
+ Max: 405
data/CHANGELOG.md CHANGED
@@ -1,12 +1,42 @@
1
1
  # Changelog
2
2
 
3
- ## [v0.3.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.3.0) (2021-02-16)
3
+ ## [v0.9.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.9.0) (2021-08-02)
4
4
 
5
- [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.2.6...v0.3.0)
5
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.8.0...v0.9.0)
6
6
 
7
7
  **Merged pull requests:**
8
8
 
9
- - SOCKS On/Off Commands [\#9](https://github.com/department-of-veterans-affairs/vtk/pull/9) ([ericboehs](https://github.com/ericboehs))
9
+ - SOCKS Setup Command [\#11](https://github.com/department-of-veterans-affairs/vtk/pull/11) ([ericboehs](https://github.com/ericboehs))
10
+ - Command Analytics [\#8](https://github.com/department-of-veterans-affairs/vtk/pull/8) ([ericboehs](https://github.com/ericboehs))
11
+
12
+ ## [v0.8.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.8.0) (2021-03-01)
13
+
14
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.7.0...v0.8.0)
15
+
16
+ **Merged pull requests:**
17
+
18
+ - Updating name [\#17](https://github.com/department-of-veterans-affairs/vtk/pull/17) ([alexpappasoddball](https://github.com/alexpappasoddball))
19
+ - Made changes to how the arguments are handled [\#15](https://github.com/department-of-veterans-affairs/vtk/pull/15) ([thilton-oddball](https://github.com/thilton-oddball))
20
+
21
+ ## [v0.7.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.7.0) (2021-03-01)
22
+
23
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.5.0...v0.7.0)
24
+
25
+ ## [v0.5.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.5.0) (2021-02-19)
26
+
27
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.4.0...v0.5.0)
28
+
29
+ ## [v0.4.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.4.0) (2021-02-19)
30
+
31
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.3.0...v0.4.0)
32
+
33
+ **Merged pull requests:**
34
+
35
+ - Added the ability to specify a name when creating a component \(i.e. controller, model, etc.\) [\#14](https://github.com/department-of-veterans-affairs/vtk/pull/14) ([thilton-oddball](https://github.com/thilton-oddball))
36
+
37
+ ## [v0.3.0](https://github.com/department-of-veterans-affairs/vtk/tree/v0.3.0) (2021-02-16)
38
+
39
+ [Full Changelog](https://github.com/department-of-veterans-affairs/vtk/compare/v0.2.6...v0.3.0)
10
40
 
11
41
  ## [v0.2.6](https://github.com/department-of-veterans-affairs/vtk/tree/v0.2.6) (2021-02-03)
12
42
 
@@ -28,6 +58,7 @@
28
58
  **Merged pull requests:**
29
59
 
30
60
  - added Docker instructions to the README [\#10](https://github.com/department-of-veterans-affairs/vtk/pull/10) ([thilton-oddball](https://github.com/thilton-oddball))
61
+ - SOCKS On/Off Commands [\#9](https://github.com/department-of-veterans-affairs/vtk/pull/9) ([ericboehs](https://github.com/ericboehs))
31
62
  - use system command [\#7](https://github.com/department-of-veterans-affairs/vtk/pull/7) ([LindseySaari](https://github.com/LindseySaari))
32
63
  - Add additional module subcommands [\#6](https://github.com/department-of-veterans-affairs/vtk/pull/6) ([LindseySaari](https://github.com/LindseySaari))
33
64
  - Check for Rails dependency [\#5](https://github.com/department-of-veterans-affairs/vtk/pull/5) ([LindseySaari](https://github.com/LindseySaari))
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Platform Developer Toolkit
1
+ # VFS Toolkit
2
2
 
3
- The purpose of this gem is to allow VFS engineers to quickly begin developing on VA.gov. It does this by providing a command line interface that allows the use of simple commands and parameters to do everything from setting up a development environment to building out a directory structure and creating necessary files for separating code into its own module.
3
+ The purpose of this gem is to allow engineers to quickly begin developing on VA.gov. It does this by providing a command line interface that allows the use of simple commands and parameters to do everything from setting up a development environment to building out a directory structure and creating necessary files for separating code into its own module.
4
4
 
5
5
  *The following assumes you have Ruby 2.6.6 or higher installed*
6
6
 
@@ -22,12 +22,12 @@ Teams developing for vets-api should create their code as a module. This allows
22
22
 
23
23
  $ vtk module add <module name>
24
24
 
25
- To add additional functionality to your module, the commands listed below are available for use. These commands can build out common functionality needed when working with a module and will be created within the given module space. When creating a new module component for a module space that does not currently exist, users will be prompted with the choice to create the module directory structure. As above, first cd into the vets-api directory, then enter the command below, substituting the name of your module for `<module name>`. You can specify the name of the module component by including `-n <component name>` after the module name. If you do not specify a module component name then the component will have the same name as the module.
25
+ To add additional functionality to your module, the commands listed below are available for use. These commands can build out common functionality needed when working with a module and will be created within the given module space. When creating a new module component for a module space that does not currently exist, users will be prompted with the choice to create the module directory structure. As above, first cd into the vets-api directory, then enter the command below, substituting the name of your component for `<component name>`. You also MUST specify the name of the module by including `-m <module name>` after the component name.
26
26
 
27
- $ vtk module controller <module name> -n <component name>
28
- $ vtk module model <module name> -n <component name>
29
- $ vtk module serializer <module name> -n <component name>
30
- $ vtk module service <module name> -n <component name>
27
+ $ vtk module controller <component name> -m <module name>
28
+ $ vtk module model <component name> -m <module name>
29
+ $ vtk module serializer <component name> -m <module name>
30
+ $ vtk module service <component name> -m <module name>
31
31
 
32
32
  This above command runs a custom rails generator. For more information see the [module generator documentation](https://github.com/department-of-veterans-affairs/vets-api/blob/master/lib/generators/module/USAGE)
33
33
 
@@ -35,6 +35,25 @@ This above command runs a custom rails generator. For more information see the [
35
35
 
36
36
  Handles connecting to VA network via SOCKS.
37
37
 
38
+ ---
39
+
40
+ ```
41
+ $ vtk socks setup
42
+ ```
43
+
44
+ The **setup subcommand** will do the following:
45
+ - Download the recommended `.ssh/config` if missing.
46
+ - Generate a VA SSH key if missing (and opens the access request form).
47
+ - Add your VA SSH key to your ssh agent and keychain.
48
+ - Test the SOCKS tunnel via SSH and HTTP
49
+ - Configure your system to start the SOCKS tunnel on boot
50
+ - Configure your system proxy for use on VA.gov domains (all other traffic bypasses the proxy).
51
+ - Allow you to troubleshoot your SOCKS connection by running it again.
52
+
53
+ **NOTE**: Running `vtk socks on` and/or `vtk socks off` is not necessary when using `vtk socks setup`.
54
+
55
+ ---
56
+
38
57
  ```
39
58
  $ vtk socks on
40
59
  ----> Connecting...
data/exe/vtk CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
6
6
  require 'vtk/cli'
7
7
 
8
8
  Signal.trap('INT') do
9
- warn("\n#{caller.join("\n")}: interrupted")
9
+ warn("\n#{caller.join("\n")}: interrupted") if ENV['DEBUG']
10
10
  exit(1)
11
11
  end
12
12
 
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'net/http'
5
+ require 'open-uri'
6
+ require 'uri'
7
+
8
+ module Vtk
9
+ # Provides command analytics to VTK team
10
+ class Analytics
11
+ attr_reader :name, :args, :hostname
12
+
13
+ def initialize(name:, args: nil, hostname: nil)
14
+ @name = name
15
+ @args = args || ARGV.join('_')
16
+ @hostname = hostname || `hostname -f`.chomp
17
+ end
18
+
19
+ def log
20
+ return if ENV['CI'] || ENV['TEST'] || ENV['VTK_DISABLE_ANALYTICS']
21
+
22
+ Process.fork do
23
+ exit unless internet?
24
+
25
+ emit_point
26
+ rescue StandardError
27
+ false # Silently error
28
+ end
29
+ end
30
+
31
+ def emit_point
32
+ uri = URI.parse 'https://dev.va.gov/_vfs/vtk-analytics/record'
33
+ Net::HTTP.start uri.host, uri.port, use_ssl: uri.scheme == 'https' do |http|
34
+ request = Net::HTTP::Post.new uri, 'Content-Type' => 'application/json'
35
+ request.body = { series: [point] }.to_json
36
+ http.request request
37
+ end
38
+ end
39
+
40
+ def point
41
+ {
42
+ metric: 'vtk.command_executed',
43
+ type: 'count',
44
+ interval: 1,
45
+ tags: ["name:#{name}", "args:#{args}"],
46
+ host: hostname,
47
+ points: [[Time.now.utc.to_i, '1']]
48
+ }
49
+ end
50
+
51
+ def internet?
52
+ true if URI.open 'http://www.google.com/'
53
+ rescue SocketError
54
+ false
55
+ end
56
+ end
57
+ end
data/lib/vtk/command.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require 'vtk/analytics'
4
5
 
5
6
  module Vtk
6
7
  # Command class that all command inherit from
@@ -9,6 +10,11 @@ module Vtk
9
10
 
10
11
  def_delegators :command, :run
11
12
 
13
+ def initialize
14
+ command_name = self.class.to_s.split('::').last(2).join('_').downcase
15
+ Vtk::Analytics.new(name: command_name).log
16
+ end
17
+
12
18
  # Execute this command
13
19
  #
14
20
  # @api public
@@ -8,9 +8,9 @@ module Vtk
8
8
  class Module < Thor
9
9
  namespace :module
10
10
 
11
- desc 'service <module name>', 'Add new service class to a module in vets-api'
11
+ desc 'service <component name>', 'Add new service class to a module in vets-api'
12
12
  method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
13
- method_option :component_name, aliases: '-n', type: :string, desc: 'Specify the service name'
13
+ method_option :module_name, aliases: '-m', type: :string, desc: 'Specify the module name', required: true
14
14
  def service(name)
15
15
  if options[:help]
16
16
  invoke :help, ['service']
@@ -20,9 +20,9 @@ module Vtk
20
20
  end
21
21
  end
22
22
 
23
- desc 'serializer <module name>', 'Add new serializer to a module in vets-api'
23
+ desc 'serializer <component name>', 'Add new serializer to a module in vets-api'
24
24
  method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
25
- method_option :component_name, aliases: '-n', type: :string, desc: 'Specify the serializer name'
25
+ method_option :module_name, aliases: '-m', type: :string, desc: 'Specify the module name', required: true
26
26
  def serializer(name)
27
27
  if options[:help]
28
28
  invoke :help, ['serializer']
@@ -32,9 +32,9 @@ module Vtk
32
32
  end
33
33
  end
34
34
 
35
- desc 'model <module name>', 'Add new model to a module in vets-api'
35
+ desc 'model <component name>', 'Add new model to a module in vets-api'
36
36
  method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
37
- method_option :component_name, aliases: '-n', type: :string, desc: 'Specify the model name'
37
+ method_option :module_name, aliases: '-m', type: :string, desc: 'Specify the module name', required: true
38
38
  def model(name)
39
39
  if options[:help]
40
40
  invoke :help, ['model']
@@ -44,9 +44,9 @@ module Vtk
44
44
  end
45
45
  end
46
46
 
47
- desc 'controller <module name>', 'Add new controller to a module in vets-api'
47
+ desc 'controller <component name>', 'Add new controller to a module in vets-api'
48
48
  method_option :help, aliases: '-h', type: :boolean, desc: 'Display usage information'
49
- method_option :component_name, aliases: '-n', type: :string, desc: 'Specify the controller name'
49
+ method_option :module_name, aliases: '-m', type: :string, desc: 'Specify the module name', required: true
50
50
  def controller(name)
51
51
  if options[:help]
52
52
  invoke :help, ['controller']
@@ -23,8 +23,8 @@ module Vtk
23
23
  private
24
24
 
25
25
  def create_controller(name, options)
26
- component_name = options[:component_name] || name
27
- system("rails g module_component #{name} method:controller component_name:#{component_name}")
26
+ module_name = options[:module_name]
27
+ system("rails g module_component #{module_name} method:controller component_name:#{name}")
28
28
  end
29
29
  end
30
30
  end
@@ -23,8 +23,8 @@ module Vtk
23
23
  private
24
24
 
25
25
  def create_model(name, options)
26
- component_name = options[:component_name] || name
27
- system("rails g module_component #{name} method:model component_name:#{component_name}")
26
+ module_name = options[:module_name]
27
+ system("rails g module_component #{module_name} method:model component_name:#{name}")
28
28
  end
29
29
  end
30
30
  end
@@ -23,8 +23,8 @@ module Vtk
23
23
  private
24
24
 
25
25
  def create_serializer(name, options)
26
- component_name = options[:component_name] || name
27
- system("rails g module_component #{name} method:serializer component_name:#{component_name}")
26
+ module_name = options[:module_name]
27
+ system("rails g module_component #{module_name} method:serializer component_name:#{name}")
28
28
  end
29
29
  end
30
30
  end
@@ -23,8 +23,8 @@ module Vtk
23
23
  private
24
24
 
25
25
  def create_service(name, options)
26
- component_name = options[:component_name] || name
27
- system("rails g module_component #{name} method:service component_name:#{component_name}")
26
+ module_name = options[:module_name]
27
+ system("rails g module_component #{module_name} method:service component_name:#{name}")
28
28
  end
29
29
  end
30
30
  end
@@ -8,6 +8,24 @@ module Vtk
8
8
  class Socks < Thor
9
9
  namespace :socks
10
10
 
11
+ desc 'setup', 'Configures local machine for VA SOCKS access'
12
+ method_option :help, aliases: '-h', type: :boolean,
13
+ desc: 'Display usage information'
14
+ method_option :boot_script_path, type: :string, desc: 'Path to install boot script (e.g. ~/Library)'
15
+ method_option :ssh_key_path, type: :string, desc: 'Path to SSH key (e.g. ~/.ssh/id_rsa_vagov)'
16
+ method_option :ssh_config_path, type: :string, desc: 'Path to SSH config (e.g. ~/.ssh/config)'
17
+ method_option :port, aliases: '-p', type: :string,
18
+ desc: 'Port that SOCKS server is running on'
19
+ method_option :skip_test, type: :boolean, desc: 'Skip testing SOCKS connection'
20
+ def setup(*)
21
+ if options[:help]
22
+ invoke :help, ['setup']
23
+ else
24
+ require_relative 'socks/setup'
25
+ Vtk::Commands::Socks::Setup.new(options).execute
26
+ end
27
+ end
28
+
11
29
  desc 'off', 'Disconnects from VA SOCKS'
12
30
  method_option :help, aliases: '-h', type: :boolean,
13
31
  desc: 'Display usage information'
@@ -50,7 +50,7 @@ module Vtk
50
50
  return output.puts "\r----> Connected to SOCKS."
51
51
  end
52
52
 
53
- output.puts "\r----> ERROR: Could not connect to SOCKS."
53
+ output.puts "\r----> ERROR: Could not connect to SOCKS. Try running `vtk socks setup` first."
54
54
  output.puts "----> Verbose Output from SSH log:\n\n"
55
55
 
56
56
  output.puts File.read '/tmp/socks.log'
@@ -0,0 +1,531 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../command'
4
+ require 'tty-prompt'
5
+ require 'fileutils'
6
+ require 'erb'
7
+
8
+ module Vtk
9
+ module Commands
10
+ class Socks
11
+ # Sets up socks access to the VA network
12
+ class Setup < Vtk::Command
13
+ PROXY_URL = 'https://raw.githubusercontent.com/department-of-veterans-affairs/va.gov-team/master/' \
14
+ 'scripts/socks/proxy.pac'
15
+
16
+ attr_reader :ssh_config_path, :input, :output, :boot_script_path, :ssh_key_path, :prompt, :port, :skip_test
17
+
18
+ def initialize(options)
19
+ @options = options
20
+ @prompt = TTY::Prompt.new interrupt: :exit
21
+ @port = options['port'] || '2001'
22
+ @boot_script_path = options['boot_script_path'] || "#{ENV['HOME']}/Library"
23
+ @ssh_key_path = options['ssh_key_path'] || "#{ENV['HOME']}/.ssh/id_rsa_vagov"
24
+ @ssh_config_path = options['ssh_config_path'] || "#{ENV['HOME']}/.ssh/config"
25
+ @skip_test = options['skip_test'] || false
26
+
27
+ super()
28
+ end
29
+
30
+ def execute(input: $stdin, output: $stdout)
31
+ define_stdin_out_vars input: input, output: output
32
+
33
+ setup_ssh_config
34
+ check_ssh_key
35
+ ssh_agent_add
36
+
37
+ unless @ssh_key_created
38
+ test_ssh_connection unless skip_test
39
+ configure_system_boot
40
+ configure_system_proxy
41
+ end
42
+
43
+ log "SOCKS setup complete. #{'Re-run `vtk socks setup` after your key is approved.' if @ssh_key_created}"
44
+ end
45
+
46
+ private
47
+
48
+ def define_stdin_out_vars(input:, output:)
49
+ @input = input
50
+ @output = output
51
+ end
52
+
53
+ def check_ssh_key
54
+ return true if key_exists? && private_and_public_keys_match?
55
+
56
+ @ssh_key_created = generate_key_and_open_key_access_request
57
+ end
58
+
59
+ def key_exists?
60
+ File.exist? ssh_key_path
61
+ end
62
+
63
+ def private_and_public_keys_match?
64
+ return true unless public_key_exists?
65
+
66
+ pub_key_from_private = `ssh-keygen -y -e -f #{ssh_key_path}`
67
+ pub_key_from_public = `ssh-keygen -y -e -f #{ssh_key_path}.pub`
68
+ return true if pub_key_from_private == pub_key_from_public
69
+
70
+ log "❌ ERROR: #{ssh_key_path}.pub is not the public key for #{ssh_key_path}."
71
+ exit 1
72
+ end
73
+
74
+ def public_key_exists?
75
+ File.exist? "#{ssh_key_path}.pub"
76
+ end
77
+
78
+ def generate_key_and_open_key_access_request
79
+ log 'VA key missing. Generating now...'
80
+ system "ssh-keygen -f #{ssh_key_path} #{'-N ""' if ENV['TEST']}"
81
+
82
+ if prompt.yes?(copy_and_open_gh)
83
+ copy_key_to_clipboard
84
+ open_command access_request_template_url
85
+ else
86
+ key_contents = File.read "#{ssh_key_path}.pub"
87
+ log "Copy this key & submit into the access request form (#{access_request_template_url}):\n#{key_contents}"
88
+ end
89
+ end
90
+
91
+ def copy_key_to_clipboard
92
+ ssh_key_contents = File.read "#{ssh_key_path}.pub"
93
+
94
+ if copy_command
95
+ IO.popen(copy_command, 'w') { |f| f << ssh_key_contents }
96
+ elsif wsl?
97
+ system %(powershell.exe Set-Clipboard -Value "'#{ssh_key_contents}'")
98
+ end
99
+ end
100
+
101
+ def copy_command
102
+ if macos?
103
+ 'pbcopy'
104
+ elsif ubuntu_like? && !wsl?
105
+ system 'sudo apt-get install -y xsel' if `which xsel`.empty?
106
+ 'xsel --clipboard'
107
+ end
108
+ end
109
+
110
+ def open_command(url)
111
+ if macos?
112
+ `open "#{url}"`
113
+ elsif wsl?
114
+ `powershell.exe Start '"#{url}"'`
115
+ elsif ubuntu_like?
116
+ `xdg-open "#{url}"`
117
+ end
118
+ end
119
+
120
+ def access_request_template_url
121
+ 'https://github.com/department-of-veterans-affairs/va.gov-team/issues/new?' \
122
+ 'assignees=&labels=external-request%2C+operations%2C+ops-access-request&' \
123
+ 'template=Environment-Access-Request-Template.md&title=Access+for+%5Bindividual%5D'
124
+ end
125
+
126
+ def copy_and_open_gh
127
+ '----> An SSH key has been created. Would you like to copy the key to your clipboard and open the access ' \
128
+ 'request issue in GitHub now?'
129
+ end
130
+
131
+ def setup_ssh_config
132
+ create_ssh_directory
133
+ install_ssh_config
134
+ configure_ssh_config_with_keychain
135
+ ssh_config_clean_up
136
+ end
137
+
138
+ def install_ssh_config
139
+ return true if ssh_config_configured?
140
+
141
+ if ssh_config_exists? && !prompt.yes?("----> #{pretty_ssh_config_path} incomplete. Backup and replace now?")
142
+ return false
143
+ end
144
+
145
+ log 'Installing SSH config...'
146
+
147
+ download_ssh_config unless File.exist? '/tmp/dova-devops'
148
+ backup_existing_ssh_config
149
+ FileUtils.cp '/tmp/dova-devops/ssh/config', ssh_config_path
150
+ FileUtils.chmod 0o600, "#{File.dirname ssh_config_path}/config"
151
+ end
152
+
153
+ def ssh_config_configured?
154
+ return false unless ssh_config_exists?
155
+
156
+ download_ssh_config
157
+ ssh_config_local = File.read ssh_config_path
158
+ ssh_config = File.read '/tmp/dova-devops/ssh/config'
159
+ ssh_config_local.include? ssh_config
160
+ end
161
+
162
+ def ssh_config_exists?
163
+ File.exist? ssh_config_path
164
+ end
165
+
166
+ def download_ssh_config
167
+ install_git
168
+
169
+ ssh_config_clean_up
170
+
171
+ ssh_agent_add
172
+ system 'git config --global credential.helper > /dev/null || ' \
173
+ "git config --global credential.helper 'cache --timeout=600'"
174
+ cloned = system(
175
+ "git clone --quiet#{' --depth 1' if macos?} --no-checkout --filter=blob:none #{repo_url} '/tmp/dova-devops'"
176
+ )
177
+ exit 1 unless cloned
178
+
179
+ `cd /tmp/dova-devops; git checkout master -- ssh/config`
180
+ end
181
+
182
+ def install_git
183
+ if macos?
184
+ install_brew
185
+ elsif ubuntu_like?
186
+ return true unless `which git`.empty?
187
+
188
+ system 'sudo apt-get install -y git'
189
+ end
190
+ end
191
+
192
+ def install_brew
193
+ return false unless macos?
194
+
195
+ installed = !`which brew`.empty?
196
+ return true if installed
197
+
198
+ log 'Homebrew not installed. Installing now...'
199
+ system '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
200
+ end
201
+
202
+ def ssh_config_clean_up
203
+ FileUtils.rm_rf '/tmp/dova-devops'
204
+ end
205
+
206
+ def repo_url
207
+ @repo_url ||= begin
208
+ keyscan_github_com
209
+
210
+ if github_ssh_configured
211
+ 'git@github.com:department-of-veterans-affairs/devops.git'
212
+ else
213
+ 'https://github.com/department-of-veterans-affairs/devops.git'
214
+ end
215
+ end
216
+ end
217
+
218
+ def keyscan_github_com
219
+ return true if File.exist?('~/.ssh/known_hosts') && !`ssh-keygen -F github.com`.empty?
220
+
221
+ `ssh-keyscan -H github.com >> ~/.ssh/known_hosts 2> /dev/null`
222
+ end
223
+
224
+ def github_ssh_configured
225
+ !`ssh -T git@github.com 2>&1`.include?('Permission denied')
226
+ end
227
+
228
+ def backup_existing_ssh_config
229
+ return true unless ssh_config_exists?
230
+
231
+ if File.exist? "#{ssh_config_path}.bak"
232
+ log "!!! ERROR: Could not make backup of #{pretty_ssh_config_path} as #{pretty_ssh_config_path}.bak " \
233
+ 'exists. Aborting.'
234
+ exit 1
235
+ end
236
+
237
+ FileUtils.mv ssh_config_path, "#{ssh_config_path}.bak"
238
+ end
239
+
240
+ def create_ssh_directory
241
+ ssh_dir = File.dirname ssh_config_path
242
+ FileUtils.mkdir_p ssh_dir
243
+ FileUtils.chmod 0o700, ssh_dir
244
+ end
245
+
246
+ def configure_ssh_config_with_keychain
247
+ return unless macos?
248
+ return if ssh_config_configured_with_keychain?
249
+
250
+ keychain_config = <<~CFG
251
+
252
+ # Maintain SSH keys in macOS Keychain
253
+ Host *
254
+ UseKeychain yes
255
+ AddKeysToAgent yes
256
+ IdentityFile #{pretty_ssh_key_path}
257
+ CFG
258
+
259
+ IO.write ssh_config_path, keychain_config, mode: 'a'
260
+ end
261
+
262
+ def ssh_config_configured_with_keychain?
263
+ return false unless ssh_config_exists?
264
+
265
+ ssh_config_local = File.readlines ssh_config_path
266
+ ssh_config_local.grep(/UseKeychain yes/).size.positive?
267
+ end
268
+
269
+ def ssh_agent_add
270
+ FileUtils.chmod 0o600, ssh_key_path if key_exists?
271
+ FileUtils.chmod 0o600, "#{ssh_key_path}.pub" if public_key_exists?
272
+
273
+ if macos?
274
+ `ssh-add -AK 2> /dev/null; ssh-add -AK #{ssh_key_path} 2> /dev/null`
275
+ elsif ubuntu_like?
276
+ `[ -z "$SSH_AUTH_SOCK" ] && eval "$(ssh-agent -s)";
277
+ ssh-add 2> /dev/null; ssh-add #{ssh_key_path} 2> /dev/null`
278
+ end
279
+ end
280
+
281
+ def test_ssh_connection
282
+ output.print '----> Testing SOCKS SSH connection...'
283
+
284
+ add_ip_to_known_hosts
285
+
286
+ if proxy_running? || ssh_output.include?('This account is currently not available.')
287
+ output.puts ' ✅ DONE'
288
+ else
289
+ check_ssh_error ssh_output
290
+ exit 1
291
+ end
292
+ end
293
+
294
+ def ssh_output
295
+ `ssh -i #{ssh_key_path} -F #{ssh_config_path} -o ConnectTimeout=5 -q socks -D #{port} exit 2>&1`
296
+ end
297
+
298
+ def add_ip_to_known_hosts
299
+ jump_box_ip = `grep -A 2 'Host socks' ~/.ssh/config | grep ProxyCommand | awk '{print $6}'`.chomp
300
+ socks_ip = `grep -A 2 'Host socks' ~/.ssh/config | grep HostName | awk '{print $2}'`.chomp
301
+
302
+ return unless `ssh-keygen -F #{socks_ip}`.empty?
303
+
304
+ `ssh-keyscan -H #{jump_box_ip} >> ~/.ssh/known_hosts 2> /dev/null`
305
+ `ssh -i #{ssh_key_path} dsva@#{jump_box_ip} 'ssh-keyscan -H #{socks_ip}' >> ~/.ssh/known_hosts 2> /dev/null`
306
+ end
307
+
308
+ def check_ssh_error(ssh_output)
309
+ if ssh_output.include? 'Permission denied (publickey)'
310
+ output.puts '⚠️ WARN: SSH key is not approved yet. Once it is, re-run `vtk socks setup`.'
311
+ copy_key_to_clipboard if prompt.yes? 'Would you like to copy your VA public key to your clipboard again?'
312
+ else
313
+ ssh_command = "ssh -i #{ssh_key_path} -F #{ssh_config_path} -o ConnectTimeout=5 -vvv socks -D #{port} -N"
314
+ output.puts ' ❌ ERROR: SSH Connection to SOCKS server unsuccessful. Error message:'
315
+ output.puts ssh_command
316
+ output.puts `#{ssh_command}`
317
+ end
318
+ end
319
+
320
+ def configure_system_boot
321
+ log 'Configuring SOCKS tunnel to run on system boot...' do
322
+ if wsl?
323
+ wsl_configure_system_boot && wsl_start_socks_proxy
324
+ else
325
+ install_autossh && (install_launch_agent || install_systemd_service)
326
+ end
327
+ end
328
+ end
329
+
330
+ def wsl_configure_system_boot
331
+ return true if File.exist? socks_bat
332
+
333
+ IO.write socks_bat, 'wsl nohup bash -c "/usr/bin/ssh socks -N &" < nul > nul 2>&1', mode: 'a'
334
+ end
335
+
336
+ def socks_bat
337
+ "#{socks_bat_dir}/gov.va.socks.bat"
338
+ end
339
+
340
+ def socks_bat_dir
341
+ profile_path = `wslpath "$(wslvar USERPROFILE)"`.chomp
342
+ "#{profile_path}/AppData/Roaming/Microsoft/Windows/Start Menu/Programs/Startup"
343
+ end
344
+
345
+ def wsl_start_socks_proxy
346
+ return true if proxy_running?
347
+
348
+ system "cd '#{socks_bat_dir}'; cmd.exe /c gov.va.socks.bat > /dev/null"
349
+ end
350
+
351
+ def proxy_running?
352
+ system("lsof -i:#{port}", out: '/dev/null') || system('lsof -nP | grep ssh | grep -q sock')
353
+ end
354
+
355
+ def launch_agent_label
356
+ @launch_agent_label ||= begin
357
+ launch_agent_label = 'gov.va.socks'
358
+ launch_agent_label += "-test-#{rand 1000}" if ENV['TEST'] == 'test'
359
+ launch_agent_label
360
+ end
361
+ end
362
+
363
+ def install_autossh
364
+ installed = !`which autossh`.empty?
365
+ return true if installed
366
+
367
+ if macos?
368
+ system 'brew install autossh'
369
+ elsif ubuntu_like?
370
+ system 'sudo apt-get install -y autossh'
371
+ end
372
+ end
373
+
374
+ def install_launch_agent
375
+ return false unless macos?
376
+
377
+ unless File.exist? "#{boot_script_path}/LaunchAgents/gov.va.socks.plist"
378
+ FileUtils.mkdir_p "#{boot_script_path}/Logs/gov.va.socks"
379
+ FileUtils.mkdir_p "#{boot_script_path}/LaunchAgents"
380
+
381
+ write_launch_agent
382
+ end
383
+
384
+ system "launchctl unload #{boot_script_path}/LaunchAgents/gov.va.socks.plist 2> /dev/null"
385
+ system "launchctl load -w #{boot_script_path}/LaunchAgents/gov.va.socks.plist"
386
+ end
387
+
388
+ def write_launch_agent
389
+ erb_template = File.read File.realpath "#{__dir__}/../../templates/socks/setup/gov.va.socks.plist.erb"
390
+ erb = ERB.new erb_template
391
+ launch_agent_contents = erb.result(
392
+ launch_agent_variables.instance_eval { binding }
393
+ )
394
+ File.write "#{boot_script_path}/LaunchAgents/gov.va.socks.plist", launch_agent_contents
395
+ end
396
+
397
+ def launch_agent_variables
398
+ OpenStruct.new(
399
+ label: launch_agent_label,
400
+ autossh_path: `which autossh`.chomp,
401
+ port: @port,
402
+ boot_script_path: File.realpath(boot_script_path),
403
+ user: ENV['USER']
404
+ )
405
+ end
406
+
407
+ def install_systemd_service
408
+ return false unless ubuntu_like?
409
+
410
+ write_systemd_service unless File.exist? '/etc/systemd/system/va_gov_socks.service'
411
+
412
+ system 'sudo systemctl daemon-reload'
413
+ system 'sudo systemctl enable va_gov_socks'
414
+ system 'sudo systemctl start va_gov_socks'
415
+ end
416
+
417
+ def write_systemd_service
418
+ erb_template = File.read File.realpath "#{__dir__}/../../templates/socks/setup/va_gov_socks.service.erb"
419
+ erb = ERB.new erb_template
420
+ systemd_service_contents = erb.result(
421
+ systemd_service_variables.instance_eval { binding }
422
+ )
423
+ File.write '/tmp/va_gov_socks.service', systemd_service_contents
424
+ system 'sudo mv /tmp/va_gov_socks.service /etc/systemd/system/va_gov_socks.service'
425
+ end
426
+
427
+ def systemd_service_variables
428
+ OpenStruct.new(
429
+ autossh_path: `which autossh`.chomp,
430
+ port: @port,
431
+ ssh_key_path: ssh_key_path,
432
+ user: ENV['USER']
433
+ )
434
+ end
435
+
436
+ def configure_system_proxy
437
+ return log 'Skipping system proxy configuration as custom --port was used.' unless port == '2001'
438
+
439
+ if macos?
440
+ mac_configure_system_proxy
441
+ elsif wsl?
442
+ wsl_configure_system_proxy
443
+ elsif ubuntu_like?
444
+ ubuntu_configure_system_proxy
445
+ end
446
+ end
447
+
448
+ def mac_configure_system_proxy
449
+ return true if mac_system_proxy_already_configured?
450
+
451
+ log 'Configuring system proxy to use SOCKS tunnel...' do
452
+ network_interfaces.map do |network_interface|
453
+ system %(networksetup -setautoproxyurl "#{network_interface}" "#{PROXY_URL}")
454
+ end.all?
455
+ end
456
+ end
457
+
458
+ def ubuntu_configure_system_proxy
459
+ return true if `gsettings get org.gnome.system.proxy mode` == "'auto'\n"
460
+
461
+ log 'Configuring system proxy to use SOCKS tunnel...' do
462
+ `gsettings set org.gnome.system.proxy mode 'auto'` &&
463
+ `gsettings set org.gnome.system.proxy autoconfig-url "#{PROXY_URL}"`
464
+ end
465
+ end
466
+
467
+ def wsl_configure_system_proxy
468
+ log 'Configuring system proxy to use SOCKS tunnel...' do
469
+ reg_key = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings'
470
+ `powershell.exe Set-ItemProperty -path "'#{reg_key}'" AutoConfigURL -Value "'#{PROXY_URL}'"`
471
+ end
472
+ end
473
+
474
+ def mac_system_proxy_already_configured?
475
+ network_interfaces.map do |network_interface|
476
+ output = `networksetup -getautoproxyurl "#{network_interface}"`
477
+ output == "URL: #{PROXY_URL}\nEnabled: Yes\n"
478
+ end.all?
479
+ end
480
+
481
+ def network_interfaces
482
+ @network_interfaces ||= begin
483
+ `networksetup -listallnetworkservices`.split("\n").drop(1).select do |network_interface|
484
+ `networksetup -getautoproxyurl "#{network_interface}"`.start_with?('URL: (null)')
485
+ end
486
+ end
487
+ end
488
+
489
+ def macos?
490
+ RUBY_PLATFORM.include? 'darwin'
491
+ end
492
+
493
+ def wsl?
494
+ @wsl ||= File.exist?('/proc/version') && File.open('/proc/version').grep(/Microsoft/).size.positive?
495
+ end
496
+
497
+ def ubuntu_like?
498
+ return false if `which apt-get`.empty? && `which gsettings`.empty?
499
+
500
+ true
501
+ end
502
+
503
+ def pretty_ssh_config_path
504
+ pretty_path ssh_config_path
505
+ end
506
+
507
+ def pretty_ssh_key_path
508
+ pretty_path ssh_key_path
509
+ end
510
+
511
+ def pretty_path(path)
512
+ path.gsub ENV['HOME'], '~'
513
+ end
514
+
515
+ def log(message)
516
+ if block_given?
517
+ output.print "----> #{message}"
518
+
519
+ return_value = yield
520
+
521
+ output.puts return_value ? ' ✅ DONE' : ' ❌ FAIL'
522
+
523
+ return_value
524
+ else
525
+ output.puts "----> #{message}"
526
+ end
527
+ end
528
+ end
529
+ end
530
+ end
531
+ end
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1,35 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string><%= label %></string>
7
+
8
+ <key>RunAtLoad</key>
9
+ <true/>
10
+
11
+ <key>ProgramArguments</key>
12
+ <array>
13
+ <string><%= autossh_path %></string>
14
+ <string>-v</string>
15
+ <string>-M</string>
16
+ <string>0</string>
17
+ <string>-D</string>
18
+ <string><%= port %></string>
19
+ <string>socks</string>
20
+ <string>-N</string>
21
+ </array>
22
+
23
+ <key>StandardOutPath</key>
24
+ <string><%= boot_script_path %>/Logs/gov.va.socks/autossh.stdout</string>
25
+
26
+ <key>StandardErrorPath</key>
27
+ <string><%= boot_script_path %>/Logs/gov.va.socks/autossh.stderr</string>
28
+
29
+ <key>User</key>
30
+ <string><%= user %></string>
31
+
32
+ <key>ThrottleInterval</key>
33
+ <integer>30</integer>
34
+ </dict>
35
+ </plist>
@@ -0,0 +1,12 @@
1
+ [Unit]
2
+ Description=VA SOCKS Tunnel
3
+ After=network.target
4
+
5
+ [Service]
6
+ Environment="AUTOSSH_GATETIME=0"
7
+ ExecStart=<%= autossh_path %> -v -M 0 -D <%= port %> -i <%= ssh_key_path %> socks -N
8
+ Restart=always
9
+ User=<%= user %>
10
+
11
+ [Install]
12
+ WantedBy=multi-user.target
data/lib/vtk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vtk
4
- VERSION = '0.5.0'
4
+ VERSION = '0.9.1'
5
5
  end
data/vtk.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = 'A CLI for the platform'
13
13
  spec.description = 'This is a platform CLI tool for VFS developer usage.'
14
14
  spec.homepage = 'https://github.com/department-of-veterans-affairs/vtk'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
18
18
  spec.metadata['documentation_uri'] = spec.homepage
@@ -29,12 +29,14 @@ Gem::Specification.new do |spec|
29
29
  spec.require_paths = ['lib']
30
30
 
31
31
  spec.add_dependency 'thor', '> 0.20.3'
32
+ spec.add_dependency 'tty-command', '~> 0.10.0'
33
+ spec.add_dependency 'tty-prompt', '~> 0.23.0'
32
34
 
33
35
  spec.add_development_dependency 'github_changelog_generator', '~> 1.15.0'
34
36
  spec.add_development_dependency 'pry', '~> 0.13.0'
35
37
  spec.add_development_dependency 'rake', '~> 13.0.0'
36
38
  spec.add_development_dependency 'rspec', '~> 3.10.0'
37
- spec.add_development_dependency 'rubocop', '~> 1.6.0'
39
+ spec.add_development_dependency 'rubocop', '~> 1.8.0'
38
40
  spec.add_development_dependency 'rubocop-rake', '~> 0.5.0'
39
41
  spec.add_development_dependency 'rubocop-rspec', '~> 2.0.0'
40
42
 
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vtk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Boehs
8
8
  - Lindsey Hattamer
9
9
  - Travis Hilton
10
- autorequire:
10
+ autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-02-19 00:00:00.000000000 Z
13
+ date: 2021-08-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: thor
@@ -26,6 +26,34 @@ dependencies:
26
26
  - - ">"
27
27
  - !ruby/object:Gem::Version
28
28
  version: 0.20.3
29
+ - !ruby/object:Gem::Dependency
30
+ name: tty-command
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: 0.10.0
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 0.10.0
43
+ - !ruby/object:Gem::Dependency
44
+ name: tty-prompt
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 0.23.0
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 0.23.0
29
57
  - !ruby/object:Gem::Dependency
30
58
  name: github_changelog_generator
31
59
  requirement: !ruby/object:Gem::Requirement
@@ -88,14 +116,14 @@ dependencies:
88
116
  requirements:
89
117
  - - "~>"
90
118
  - !ruby/object:Gem::Version
91
- version: 1.6.0
119
+ version: 1.8.0
92
120
  type: :development
93
121
  prerelease: false
94
122
  version_requirements: !ruby/object:Gem::Requirement
95
123
  requirements:
96
124
  - - "~>"
97
125
  - !ruby/object:Gem::Version
98
- version: 1.6.0
126
+ version: 1.8.0
99
127
  - !ruby/object:Gem::Dependency
100
128
  name: rubocop-rake
101
129
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +167,7 @@ files:
139
167
  - ".gitignore"
140
168
  - ".rspec"
141
169
  - ".rubocop.yml"
170
+ - ".rubocop_todo.yml"
142
171
  - ".tool-versions"
143
172
  - CHANGELOG.md
144
173
  - Gemfile
@@ -150,6 +179,7 @@ files:
150
179
  - docs/design-doc.md
151
180
  - exe/vtk
152
181
  - lib/vtk.rb
182
+ - lib/vtk/analytics.rb
153
183
  - lib/vtk/cli.rb
154
184
  - lib/vtk/command.rb
155
185
  - lib/vtk/commands/.gitkeep
@@ -162,8 +192,12 @@ files:
162
192
  - lib/vtk/commands/socks.rb
163
193
  - lib/vtk/commands/socks/off.rb
164
194
  - lib/vtk/commands/socks/on.rb
195
+ - lib/vtk/commands/socks/setup.rb
165
196
  - lib/vtk/templates/.gitkeep
166
197
  - lib/vtk/templates/module/add/.gitkeep
198
+ - lib/vtk/templates/socks/setup/.gitkeep
199
+ - lib/vtk/templates/socks/setup/gov.va.socks.plist.erb
200
+ - lib/vtk/templates/socks/setup/va_gov_socks.service.erb
167
201
  - lib/vtk/version.rb
168
202
  - vtk.gemspec
169
203
  homepage: https://github.com/department-of-veterans-affairs/vtk
@@ -174,7 +208,7 @@ metadata:
174
208
  documentation_uri: https://github.com/department-of-veterans-affairs/vtk
175
209
  source_code_uri: https://github.com/department-of-veterans-affairs/vtk
176
210
  changelog_uri: https://github.com/department-of-veterans-affairs/vtk/blob/master/CHANGELOG.md
177
- post_install_message:
211
+ post_install_message:
178
212
  rdoc_options: []
179
213
  require_paths:
180
214
  - lib
@@ -182,15 +216,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
216
  requirements:
183
217
  - - ">="
184
218
  - !ruby/object:Gem::Version
185
- version: 2.4.0
219
+ version: 2.5.0
186
220
  required_rubygems_version: !ruby/object:Gem::Requirement
187
221
  requirements:
188
222
  - - ">="
189
223
  - !ruby/object:Gem::Version
190
224
  version: '0'
191
225
  requirements: []
192
- rubygems_version: 3.0.3
193
- signing_key:
226
+ rubygems_version: 3.2.1
227
+ signing_key:
194
228
  specification_version: 4
195
229
  summary: A CLI for the platform
196
230
  test_files: []