sshkit-sudo-next 0.2.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eaf0ceec3960adf1b245082ce6c353db05abf9e5a8941ed267454eb0a6b9683f
4
+ data.tar.gz: 64d65fd50dc8624827c2479c898f4c4a294bbb26814c204c613dfe5fb5d2d9cf
5
+ SHA512:
6
+ metadata.gz: 061ddb73088198fd52acabba6a3db5d66ee7d965f8d34eaf1aef29af62e45bc41ef41a10565dca26cf85d132112da400534c22c507764f0dd315626190f6cf5f
7
+ data.tar.gz: a09448741593b69288b0f32cb1428eafd25e009063f651f8759f8614eb31b1ddd3b0267aa21a5cdd4a119bac250e75e5a28b463ec584fd44944faecf4b16056f
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sshkit-sudo-next.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright for portions of project ssh-sudo-next are held by Kentaro Imai, 2015 as part of project Bar. All other copyright for project Foo are held by Saverio Miroddi, 2021.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # SSHKit::Sudo ("next" version)
2
+
3
+ Fork of the [SSHKit::Sudo](https://github.com/kentaroi/sshkit-sudo.git) project, with significant cleanups and some improvements.
4
+
5
+ As the original project, this gem allows:
6
+
7
+ - sudo commands with password support;
8
+ - perform all the tasks of a given session (eg. a whole deploy) as a specified user.
9
+
10
+ We're in the process of preparing and releasing thorough documentation (estimated time: beginning of May/2021).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,5 @@
1
+ require "sshkit/sudo/version"
2
+
3
+ require 'sshkit/sudo/command'
4
+ require 'sshkit/sudo/password_sending_interaction_handler'
5
+ require 'sshkit/sudo/sudo_netssh'
@@ -0,0 +1,14 @@
1
+ require 'sshkit'
2
+
3
+ # This is cosmetic, although it considerably improves the output. See https://github.com/capistrano/sshkit/issues/490.
4
+ #
5
+ module SSHKit
6
+ class Command
7
+ def with(&_block)
8
+ return yield if options[:user]
9
+ env_string = environment_string
10
+ return yield if env_string.empty?
11
+ "( export #{env_string} ; #{yield} )"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,28 @@
1
+ module SSHKit
2
+ module Sudo
3
+ # Slightly simplified version of the original:
4
+ #
5
+ # - assumed that there is a password
6
+ # - assumes the same pwd for the hosts
7
+ # - replaced the class/dead methods with constants
8
+ # - removed the InteractionHandler subclass
9
+ #
10
+ class PasswordSendingInteractionHandler
11
+ WRONG_PASSWORD_MESSAGE_REGEX = /Sorry.*\stry\sagain/
12
+ PASSWORD_PROMPT_REGEX = /[Pp]assword.*:/
13
+
14
+ def initialize(servers_password)
15
+ @servers_password = servers_password
16
+ end
17
+
18
+ def on_data(command, stream_name, data, channel)
19
+ raise "Wrong password!" if data =~ WRONG_PASSWORD_MESSAGE_REGEX
20
+
21
+ if data =~ PASSWORD_PROMPT_REGEX
22
+ pass = @servers_password
23
+ channel.send_data(pass)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,155 @@
1
+ require 'sshkit'
2
+ require_relative 'password_sending_interaction_handler'
3
+ require_relative 'command'
4
+
5
+ module SSHKit
6
+ module Backend
7
+ # Values that are static, like the filtered logging patterns, or the name of the env variables,
8
+ # can be implemented, if needed, as Configuration attributes.
9
+ #
10
+ class SudoNetssh < Netssh
11
+ PASSWORD_PROMPT_REGEX = /\[sudo\] password for \S+\:/
12
+
13
+ SKIP_STDOUT_LOGGING_PATTERNS = [
14
+ /^\r\n$/,
15
+ PASSWORD_PROMPT_REGEX, # Skipping this removes context from the wrong password prompt, but it's
16
+ # still understandable.
17
+ ]
18
+
19
+ class << self
20
+ # `pool` is a class-level instance variable, so we can't use the superclass' attr_accessor.
21
+ # The attribute is only read though, so we don't need to handle assignments.
22
+ #
23
+ def pool
24
+ self.superclass.pool
25
+ end
26
+
27
+ # It's not possible to send a custom configuration class, so we need to create a custom one.
28
+ # The :owner could be an instance variable, but it's a bit ugly to use a different setting
29
+ # strategy.
30
+ #
31
+ def config
32
+ @config ||= Class.new(Netssh::Configuration) do
33
+ attr_accessor :owner, :servers_password, :commands_log
34
+ end.new
35
+ end
36
+ end
37
+
38
+ def initialize(*args, &block)
39
+ super
40
+
41
+ @interaction_handler = SSHKit::Sudo::PasswordSendingInteractionHandler.new(self.class.config.servers_password + "\n")
42
+ end
43
+
44
+ def capture(*args)
45
+ # To ensure that we clean out the sudo part when the results are returned,
46
+ # otherwise the commands will be corrupt.
47
+ #
48
+ super.gsub(PASSWORD_PROMPT_REGEX, '')
49
+ end
50
+
51
+ # Required because the uploaded file is owned by the SSH user, not the owner.
52
+ #
53
+ def upload!(local, remote, options = {})
54
+ super
55
+
56
+ # We can't check the user inside the :as block, because @user is root.
57
+ #
58
+ target_user = @user || self.class.config.owner
59
+
60
+ as :root do
61
+ execute :chown, target_user, remote
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def command(args, options)
68
+ options[:interaction_handler] ||= @interaction_handler
69
+
70
+ env = (@env || {})
71
+
72
+ user = @user || self.class.config.owner
73
+
74
+ # As general Linux practice, switching user is not enough - some variables need to be updated
75
+ # as well.
76
+ # For an explanation, see https://saveriomiroddi.github.io/Chef-properly-run-a-resource-as-alternate-user.
77
+ #
78
+ user_home = user == 'root' ? '/root' : "/home/#{user}"
79
+ env = env.merge(user: user, home: user_home)
80
+
81
+ # Sshkit::Command#user runs commands in a non-login shell, so that variables are not inherited.
82
+ # We can workaround this by setting them in the env, which is `export`ed, however, only the
83
+ # specified ones are. Since `RAILS_ENV` is common, we pass it.
84
+ #
85
+ env = env.merge(rails_env: fetch(:rails_env))
86
+
87
+ SSHKit::Command.new(*args, options.merge(
88
+ {
89
+ in: pwd_path,
90
+ env: env,
91
+ host: @host,
92
+ user: user,
93
+ group: @group,
94
+ }
95
+ ))
96
+ end
97
+
98
+ def execute_command(cmd)
99
+ output.log_command_start(cmd)
100
+ cmd.started = true
101
+ exit_status = nil
102
+ with_ssh do |ssh|
103
+ ssh.open_channel do |chan|
104
+ chan.request_pty
105
+ prepared_command = cmd.to_command
106
+
107
+ if self.class.config.commands_log
108
+ IO.write(self.class.config.commands_log, prepared_command + "\n", mode: 'a')
109
+ end
110
+
111
+ chan.exec prepared_command do |_ch, _success|
112
+ chan.on_data do |ch, data|
113
+ cmd.on_stdout(ch, data)
114
+ skip_stdout_logging = SKIP_STDOUT_LOGGING_PATTERNS.any? { |pattern| data =~ pattern }
115
+ output.log_command_data(cmd, :stdout, data) unless skip_stdout_logging
116
+ end
117
+ chan.on_extended_data do |ch, _type, data|
118
+ cmd.on_stderr(ch, data)
119
+ output.log_command_data(cmd, :stderr, data)
120
+ end
121
+ chan.on_request("exit-status") do |_ch, data|
122
+ exit_status = data.read_long
123
+ end
124
+ #chan.on_request("exit-signal") do |ch, data|
125
+ # # TODO: This gets called if the program is killed by a signal
126
+ # # might also be a worthwhile thing to report
127
+ # exit_signal = data.read_string.to_i
128
+ # warn ">>> " + exit_signal.inspect
129
+ # output.log_command_killed(cmd, exit_signal)
130
+ #end
131
+ chan.on_open_failed do |_ch|
132
+ # TODO: What do do here?
133
+ # I think we should raise something
134
+ end
135
+ chan.on_process do |_ch|
136
+ # TODO: I don't know if this is useful
137
+ end
138
+ chan.on_eof do |_ch|
139
+ # TODO: chan sends EOF before the exit status has been
140
+ # writtend
141
+ end
142
+ end
143
+ chan.wait
144
+ end
145
+ ssh.loop
146
+ end
147
+ # Set exit_status and log the result upon completion
148
+ if exit_status
149
+ cmd.exit_status = exit_status
150
+ output.log_command_exit(cmd)
151
+ end
152
+ end
153
+ end # class SudoNetssh
154
+ end # module Backend
155
+ end # module SSHKit
@@ -0,0 +1,5 @@
1
+ module SSHKit
2
+ module Sudo
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sshkit/sudo/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sshkit-sudo-next"
8
+ spec.version = SSHKit::Sudo::VERSION
9
+ spec.authors = ["Saverio Miroddi"]
10
+ spec.email = ["saverio.pub2@gmail.com"]
11
+ spec.summary = %q{SSHKit extension, for sudo operation with password input.}
12
+ spec.description = %q{SSHKit extension, for sudo operation with password input.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 1.9.3"
22
+
23
+ spec.add_dependency "sshkit", "~> 1.20.0"
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sshkit-sudo-next
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Saverio Miroddi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sshkit
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.20.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.20.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description: SSHKit extension, for sudo operation with password input.
56
+ email:
57
+ - saverio.pub2@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - lib/sshkit/sudo.rb
68
+ - lib/sshkit/sudo/command.rb
69
+ - lib/sshkit/sudo/password_sending_interaction_handler.rb
70
+ - lib/sshkit/sudo/sudo_netssh.rb
71
+ - lib/sshkit/sudo/version.rb
72
+ - sshkit-sudo-next.gemspec
73
+ homepage: ''
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: 1.9.3
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.1.4
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: SSHKit extension, for sudo operation with password input.
96
+ test_files: []