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 +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +10 -0
- data/Rakefile +2 -0
- data/lib/sshkit/sudo.rb +5 -0
- data/lib/sshkit/sudo/command.rb +14 -0
- data/lib/sshkit/sudo/password_sending_interaction_handler.rb +28 -0
- data/lib/sshkit/sudo/sudo_netssh.rb +155 -0
- data/lib/sshkit/sudo/version.rb +5 -0
- data/sshkit-sudo-next.gemspec +26 -0
- metadata +96 -0
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
data/Gemfile
ADDED
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
data/lib/sshkit/sudo.rb
ADDED
@@ -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,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: []
|