ssh_tunnels 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b02037bed34da5ad832e27db6175c5cfb9dc22ed945aea8b06132d11b3b1cf40
4
- data.tar.gz: afdc8849d8297603f943056c32bfbd75a20f02ab6ac420f99411193570b1ac52
3
+ metadata.gz: 9bcdb9b758ed9d27d0e81ba9bafbf8e50273fa38c2c954b332b916fccb7cc699
4
+ data.tar.gz: 42d49d0040704dbe2ca4c51040ccfffc797eb2f5cf4dbdc7c322aa51a9f26c27
5
5
  SHA512:
6
- metadata.gz: 448766cc0443b44c59798e1f1efc71a5e6f3e955f778df03ce1682a7d7467ab42e0c390aaff4524697f7cba8fc10e936dd16e93570efcafcf2604023146065af
7
- data.tar.gz: c54e62eb16ad72a4ef0698be1a5b1a2b9b8e07b09d4d574df444194a3f83b755ef18477af1e04f7ce8b9a7fd8d6117657d39d3c93741af0a7c22d83e47dc83bc
6
+ metadata.gz: 109da60813a282a22c54b91075112e9a2bf5408e2490abe7c625aae87ef7c45ef3633ab698cdfd5f82fa24cf2a0fae04e32a6c86ba9a66b275b2eba97621e4c3
7
+ data.tar.gz: 117a4e60567f1763cf7c55ca024e4a1a9fc00e4b187209aff3e57edb288b209dd18ab217e958d505a4837b5daab1a8bd9502e098d1640080c091633b60b6f282
data/.rubocop.yml ADDED
@@ -0,0 +1,4 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.3
4
+ SuggestExtensions: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.7
1
+ 3.3.9
data/Gemfile CHANGED
@@ -3,3 +3,9 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
+
7
+ group :development do
8
+ gem 'rspec', '~> 3.13'
9
+ gem 'rubocop', '~> 1.86'
10
+ gem 'strong_versions', '~> 0.4.5'
11
+ end
data/Gemfile.lock CHANGED
@@ -1,73 +1,79 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ssh_tunnels (0.2.1)
4
+ ssh_tunnels (0.3.0)
5
5
  bcrypt_pbkdf (~> 1.1)
6
- curses (~> 1.3)
6
+ curses (~> 1.4)
7
7
  ed25519 (~> 1.3)
8
- net-ssh-gateway (~> 2.0)
8
+ net-ssh (~> 7.0)
9
9
 
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- ast (2.4.2)
14
- bcrypt_pbkdf (1.1.0)
15
- concurrent-ruby (1.2.3)
16
- curses (1.4.4)
17
- diff-lcs (1.5.1)
18
- ed25519 (1.3.0)
19
- i18n (1.14.4)
13
+ ast (2.4.3)
14
+ bcrypt_pbkdf (1.1.2)
15
+ concurrent-ruby (1.3.6)
16
+ curses (1.6.0)
17
+ diff-lcs (1.6.2)
18
+ ed25519 (1.4.0)
19
+ i18n (1.14.8)
20
20
  concurrent-ruby (~> 1.0)
21
- net-ssh (7.2.3)
22
- net-ssh-gateway (2.0.0)
23
- net-ssh (>= 4.0.0)
21
+ json (2.19.7)
22
+ language_server-protocol (3.17.0.5)
23
+ lint_roller (1.1.0)
24
+ net-ssh (7.3.2)
24
25
  paint (2.3.0)
25
- parallel (1.24.0)
26
- parser (3.3.0.5)
26
+ parallel (2.1.0)
27
+ parser (3.3.11.1)
27
28
  ast (~> 2.4.1)
28
29
  racc
29
- racc (1.7.3)
30
+ prism (1.9.0)
31
+ racc (1.8.1)
30
32
  rainbow (3.1.1)
31
- regexp_parser (2.9.0)
32
- rexml (3.2.6)
33
- rspec (3.13.0)
33
+ regexp_parser (2.12.0)
34
+ rspec (3.13.2)
34
35
  rspec-core (~> 3.13.0)
35
36
  rspec-expectations (~> 3.13.0)
36
37
  rspec-mocks (~> 3.13.0)
37
- rspec-core (3.13.0)
38
+ rspec-core (3.13.6)
38
39
  rspec-support (~> 3.13.0)
39
- rspec-expectations (3.13.0)
40
+ rspec-expectations (3.13.5)
40
41
  diff-lcs (>= 1.2.0, < 2.0)
41
42
  rspec-support (~> 3.13.0)
42
- rspec-mocks (3.13.0)
43
+ rspec-mocks (3.13.8)
43
44
  diff-lcs (>= 1.2.0, < 2.0)
44
45
  rspec-support (~> 3.13.0)
45
- rspec-support (3.13.1)
46
- rubocop (0.86.0)
47
- parallel (~> 1.10)
48
- parser (>= 2.7.0.1)
46
+ rspec-support (3.13.7)
47
+ rubocop (1.86.2)
48
+ json (~> 2.3)
49
+ language_server-protocol (~> 3.17.0.2)
50
+ lint_roller (~> 1.1.0)
51
+ parallel (>= 1.10)
52
+ parser (>= 3.3.0.2)
49
53
  rainbow (>= 2.2.2, < 4.0)
50
- regexp_parser (>= 1.7)
51
- rexml
52
- rubocop-ast (>= 0.0.3, < 1.0)
54
+ regexp_parser (>= 2.9.3, < 3.0)
55
+ rubocop-ast (>= 1.49.0, < 2.0)
53
56
  ruby-progressbar (~> 1.7)
54
- unicode-display_width (>= 1.4.0, < 2.0)
55
- rubocop-ast (0.8.0)
56
- parser (>= 2.7.1.5)
57
+ unicode-display_width (>= 2.4.0, < 4.0)
58
+ rubocop-ast (1.49.1)
59
+ parser (>= 3.3.7.2)
60
+ prism (~> 1.7)
57
61
  ruby-progressbar (1.13.0)
58
62
  strong_versions (0.4.5)
59
63
  i18n (>= 0.5)
60
64
  paint (~> 2.0)
61
- unicode-display_width (1.8.0)
65
+ unicode-display_width (3.2.0)
66
+ unicode-emoji (~> 4.1)
67
+ unicode-emoji (4.2.0)
62
68
 
63
69
  PLATFORMS
64
70
  ruby
65
71
 
66
72
  DEPENDENCIES
67
- rspec (~> 3.9)
68
- rubocop (~> 0.86.0)
73
+ rspec (~> 3.13)
74
+ rubocop (~> 1.86)
69
75
  ssh_tunnels!
70
- strong_versions (~> 0.4.4)
76
+ strong_versions (~> 0.4.5)
71
77
 
72
78
  BUNDLED WITH
73
- 2.1.4
79
+ 4.0.12
data/README.md CHANGED
@@ -63,6 +63,17 @@ The `tunnels` section is a map where each key represents a named tunnel. Each tu
63
63
  * `host`: The remote host to connect to from the gateway.
64
64
  * `remote`: The remote port to use for forwarding.
65
65
  * `local`: The local port to bind to (defaults to the `remote` port).
66
+ * `local_ip`: The local IP address to bind to (optional, defaults to the top-level `default_local_ip` if set, otherwise `127.0.0.1`). Equivalent to the `<local-ip>` component in `ssh -L <local-ip>:<local-port>:<remote-host>:<remote-port>`. Takes precedence over `default_local_ip`.
67
+
68
+ ### Default local IP
69
+
70
+ The top-level `default_local_ip` option sets the local bind IP address used for all tunnels that do not specify their own `local_ip`:
71
+
72
+ ```yaml
73
+ # config.yml
74
+
75
+ default_local_ip: 0.0.0.0
76
+ ```
66
77
 
67
78
  ```yaml
68
79
  # config.yml
@@ -85,6 +96,11 @@ tunnels:
85
96
  local: 1111
86
97
  host: other.host.example.com
87
98
  remote: 5555
99
+ bound_host:
100
+ local_ip: 192.168.1.100
101
+ local: 2222
102
+ host: internal.host.example.com
103
+ remote: 8080
88
104
  ```
89
105
 
90
106
  ## Contributing
data/bin/ssh_tunnels CHANGED
@@ -27,11 +27,12 @@ unless File.exist?(config_path)
27
27
  exit 1
28
28
  end
29
29
 
30
- config = YAML.safe_load(File.read(config_path))
30
+ config = YAML.safe_load_file(config_path)
31
31
 
32
32
  begin
33
33
  default_gateway = config.fetch('default_gateway', nil)
34
- if !config.key?('tunnels')
34
+ default_local_ip = config.fetch('default_local_ip', nil)
35
+ unless config.key?('tunnels')
35
36
  warn('Configuration file must provide `tunnels` section. Exiting.')
36
37
  exit 1
37
38
  end
@@ -51,14 +52,14 @@ begin
51
52
  end
52
53
 
53
54
  if error
54
- warn("Configuration errors detected. Exiting.")
55
+ warn('Configuration errors detected. Exiting.')
55
56
  exit 1
56
57
  end
57
58
  end
58
59
 
59
60
  user = ENV.fetch('USER')
60
61
  print 'Enter SSH key passphrase (leave blank if not required): '
61
- passphrase = STDIN.noecho(&:gets).chomp
62
+ passphrase = $stdin.noecho(&:gets).chomp
62
63
  puts
63
64
 
64
65
  begin
@@ -68,7 +69,8 @@ begin
68
69
  else
69
70
  default_gateway
70
71
  end
71
- SshTunnels::Tunnel.new(name, user, tunnel_config, gateway, passphrase)
72
+ config = { 'local_ip' => default_local_ip }.merge(tunnel_config)
73
+ SshTunnels::Tunnel.new(name, user, config, gateway, passphrase)
72
74
  end
73
75
  ui = SshTunnels::UI.new(tunnels)
74
76
  ui.run
@@ -10,13 +10,18 @@ module SshTunnels
10
10
  @user = user
11
11
  @config = config
12
12
  @passphrase = passphrase
13
- @config = config
14
13
  @gateway = gateway
15
- @connection = nil
14
+ @session = nil
15
+ @thread = nil
16
+ @active = false
16
17
  end
17
18
 
18
19
  def to_s
19
- base = "#{local_port}:#{remote_host}:#{remote_port}"
20
+ base = if local_host
21
+ "#{local_host}:#{local_port}:#{remote_host}:#{remote_port}"
22
+ else
23
+ "#{local_port}:#{remote_host}:#{remote_port}"
24
+ end
20
25
  return base unless @error
21
26
 
22
27
  "#{base} (#{@error})"
@@ -27,25 +32,35 @@ module SshTunnels
27
32
  end
28
33
 
29
34
  def open
30
- @connection = Net::SSH::Gateway.new(@gateway.fetch('host'), @gateway.fetch('user', @user), options)
31
- @connection.open(remote_host, remote_port, local_port)
32
- rescue StandardError => e
35
+ @session = Net::SSH.start(@gateway.fetch('host'), @gateway.fetch('user', @user), options)
36
+ forward_local
37
+ @active = true
38
+ @thread = Thread.new { @session.loop(0.001) { @active } }
39
+ rescue StandardError
33
40
  shutdown
34
41
  raise
35
42
  end
36
43
 
37
44
  def active?
38
- return false if @connection.nil?
39
-
40
- @connection.active?
45
+ @active && @thread&.alive?
41
46
  end
42
47
 
43
48
  def shutdown
44
- @connection&.shutdown!
49
+ @active = false
50
+ @thread&.join
51
+ @session&.close
52
+ @session = nil
53
+ @thread = nil
45
54
  end
46
55
 
47
56
  private
48
57
 
58
+ def forward_local
59
+ args = [local_port, remote_host, remote_port]
60
+ args.unshift(local_host) if local_host
61
+ @session.forward.local(*args)
62
+ end
63
+
49
64
  def remote_host
50
65
  @config.fetch('host')
51
66
  end
@@ -54,6 +69,10 @@ module SshTunnels
54
69
  @config.fetch('remote_port')
55
70
  end
56
71
 
72
+ def local_host
73
+ @config.fetch('local_ip', nil)
74
+ end
75
+
57
76
  def local_port
58
77
  @config.fetch('local_port', remote_port)
59
78
  end
@@ -91,16 +91,14 @@ module SshTunnels
91
91
  tunnel = @tunnels[IDENTIFIERS.index { |value| value == input }]
92
92
  return status("Unrecognized tunnel: #{input}") if tunnel.nil?
93
93
 
94
- if tunnel.active?
95
- status("Disconnecting: #{tunnel}")
96
- else
97
- status("Connecting: #{tunnel}")
98
- end
99
- begin
100
- tunnel.toggle
101
- rescue StandardError => e
102
- status("Error: #{e}")
103
- end
94
+ toggle_tunnel(tunnel)
95
+ end
96
+
97
+ def toggle_tunnel(tunnel)
98
+ status("#{tunnel.active? ? 'Disconnecting' : 'Connecting'}: #{tunnel}")
99
+ tunnel.toggle
100
+ rescue StandardError => e
101
+ status("Error: #{e}")
104
102
  end
105
103
 
106
104
  def tunnel_color(tunnel)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SshTunnels
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/ssh_tunnels.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'curses'
4
- require 'net/ssh/gateway'
4
+ require 'net/ssh'
5
5
 
6
6
  require 'ssh_tunnels/version'
7
7
  require 'ssh_tunnels/tunnel'
data/ssh_tunnels.gemspec CHANGED
@@ -12,11 +12,12 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'Conveniently manage numerous SSH tunnels with ncurses'
13
13
  spec.homepage = 'https://github.com/bobf/ssh_tunnels'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.3.0')
16
16
 
17
17
  spec.metadata['homepage_uri'] = spec.homepage
18
18
  spec.metadata['source_code_uri'] = spec.homepage
19
19
  spec.metadata['changelog_uri'] = 'https://github.com/bobf/ssh_tunnels/blob/master/CHANGELOG.md'
20
+ spec.metadata['rubygems_mfa_required'] = 'true'
20
21
 
21
22
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
23
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -25,12 +26,8 @@ Gem::Specification.new do |spec|
25
26
  spec.executables = 'ssh_tunnels'
26
27
  spec.require_paths = ['lib']
27
28
 
28
- spec.add_runtime_dependency 'curses', '~> 1.3'
29
- spec.add_runtime_dependency 'net-ssh-gateway', '~> 2.0'
30
- spec.add_runtime_dependency 'ed25519', '~> 1.3'
31
- spec.add_runtime_dependency 'bcrypt_pbkdf', '~> 1.1'
32
-
33
- spec.add_development_dependency 'rspec', '~> 3.9'
34
- spec.add_development_dependency 'rubocop', '~> 0.86.0'
35
- spec.add_development_dependency 'strong_versions', '~> 0.4.4'
29
+ spec.add_dependency 'bcrypt_pbkdf', '~> 1.1'
30
+ spec.add_dependency 'curses', '~> 1.4'
31
+ spec.add_dependency 'ed25519', '~> 1.3'
32
+ spec.add_dependency 'net-ssh', '~> 7.0'
36
33
  end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ssh_tunnels
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-10 00:00:00.000000000 Z
11
+ date: 2026-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: curses
14
+ name: bcrypt_pbkdf
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
- name: net-ssh-gateway
28
+ name: curses
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.0'
33
+ version: '1.4'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.0'
40
+ version: '1.4'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: ed25519
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,61 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: bcrypt_pbkdf
56
+ name: net-ssh
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.1'
61
+ version: '7.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.1'
69
- - !ruby/object:Gem::Dependency
70
- name: rspec
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.9'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.9'
83
- - !ruby/object:Gem::Dependency
84
- name: rubocop
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: 0.86.0
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: 0.86.0
97
- - !ruby/object:Gem::Dependency
98
- name: strong_versions
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 0.4.4
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 0.4.4
68
+ version: '7.0'
111
69
  description: Conveniently manage numerous SSH tunnels with ncurses
112
70
  email:
113
71
  - git@bob.frl
@@ -118,6 +76,7 @@ extra_rdoc_files: []
118
76
  files:
119
77
  - ".gitignore"
120
78
  - ".rspec"
79
+ - ".rubocop.yml"
121
80
  - ".ruby-version"
122
81
  - Gemfile
123
82
  - Gemfile.lock
@@ -141,6 +100,7 @@ metadata:
141
100
  homepage_uri: https://github.com/bobf/ssh_tunnels
142
101
  source_code_uri: https://github.com/bobf/ssh_tunnels
143
102
  changelog_uri: https://github.com/bobf/ssh_tunnels/blob/master/CHANGELOG.md
103
+ rubygems_mfa_required: 'true'
144
104
  post_install_message:
145
105
  rdoc_options: []
146
106
  require_paths:
@@ -149,14 +109,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
109
  requirements:
150
110
  - - ">="
151
111
  - !ruby/object:Gem::Version
152
- version: 2.3.0
112
+ version: 3.3.0
153
113
  required_rubygems_version: !ruby/object:Gem::Requirement
154
114
  requirements:
155
115
  - - ">="
156
116
  - !ruby/object:Gem::Version
157
117
  version: '0'
158
118
  requirements: []
159
- rubygems_version: 3.4.13
119
+ rubygems_version: 3.5.22
160
120
  signing_key:
161
121
  specification_version: 4
162
122
  summary: Interactive SSH tunnel management