sshkit-sudo-next 0.2.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
+ 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: []