Merge pull request #6185 from marc-ta/elevated_interactive
Elevated interactive
This commit is contained in:
commit
37940e7350
|
@ -136,10 +136,11 @@ module VagrantPlugins
|
||||||
error_key: nil, # use the error_class message key
|
error_key: nil, # use the error_class message key
|
||||||
good_exit: 0,
|
good_exit: 0,
|
||||||
shell: :powershell,
|
shell: :powershell,
|
||||||
|
interactive: false,
|
||||||
}.merge(opts || {})
|
}.merge(opts || {})
|
||||||
|
|
||||||
opts[:good_exit] = Array(opts[:good_exit])
|
opts[:good_exit] = Array(opts[:good_exit])
|
||||||
command = wrap_in_scheduled_task(command) if opts[:elevated]
|
command = wrap_in_scheduled_task(command, opts[:interactive]) if opts[:elevated]
|
||||||
output = shell.send(opts[:shell], command, &block)
|
output = shell.send(opts[:shell], command, &block)
|
||||||
execution_output(output, opts)
|
execution_output(output, opts)
|
||||||
end
|
end
|
||||||
|
@ -193,9 +194,11 @@ module VagrantPlugins
|
||||||
# in place.
|
# in place.
|
||||||
#
|
#
|
||||||
# @return The wrapper command to execute
|
# @return The wrapper command to execute
|
||||||
def wrap_in_scheduled_task(command)
|
def wrap_in_scheduled_task(command, interactive)
|
||||||
path = File.expand_path("../scripts/elevated_shell.ps1", __FILE__)
|
path = File.expand_path("../scripts/elevated_shell.ps1", __FILE__)
|
||||||
script = Vagrant::Util::TemplateRenderer.render(path)
|
script = Vagrant::Util::TemplateRenderer.render(path, options: {
|
||||||
|
interactive: interactive,
|
||||||
|
})
|
||||||
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
|
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
|
||||||
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
|
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -13,7 +13,7 @@ $task_xml = @'
|
||||||
<Principals>
|
<Principals>
|
||||||
<Principal id="Author">
|
<Principal id="Author">
|
||||||
<UserId>{username}</UserId>
|
<UserId>{username}</UserId>
|
||||||
<LogonType>Password</LogonType>
|
<LogonType><%= options[:interactive] ? 'InteractiveTokenOrPassword' : 'Password' %></LogonType>
|
||||||
<RunLevel>HighestAvailable</RunLevel>
|
<RunLevel>HighestAvailable</RunLevel>
|
||||||
</Principal>
|
</Principal>
|
||||||
</Principals>
|
</Principals>
|
||||||
|
@ -55,7 +55,7 @@ $schedule.Connect()
|
||||||
$task = $schedule.NewTask($null)
|
$task = $schedule.NewTask($null)
|
||||||
$task.XmlText = $task_xml
|
$task.XmlText = $task_xml
|
||||||
$folder = $schedule.GetFolder("\")
|
$folder = $schedule.GetFolder("\")
|
||||||
$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, 1, $null) | Out-Null
|
$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, <%= options[:interactive] ? 3 : 1 %>, $null) | Out-Null
|
||||||
|
|
||||||
$registered_task = $folder.GetTask("\$task_name")
|
$registered_task = $folder.GetTask("\$task_name")
|
||||||
$registered_task.Run($null) | Out-Null
|
$registered_task.Run($null) | Out-Null
|
||||||
|
@ -71,7 +71,7 @@ function SlurpOutput($out_file, $cur_line) {
|
||||||
if (Test-Path $out_file) {
|
if (Test-Path $out_file) {
|
||||||
get-content $out_file | select -skip $cur_line | ForEach {
|
get-content $out_file | select -skip $cur_line | ForEach {
|
||||||
$cur_line += 1
|
$cur_line += 1
|
||||||
Write-Host "$_"
|
Write-Host "$_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $cur_line
|
return $cur_line
|
||||||
|
|
|
@ -23,6 +23,7 @@ module VagrantPlugins
|
||||||
HTTPClient::KeepAliveDisconnected,
|
HTTPClient::KeepAliveDisconnected,
|
||||||
WinRM::WinRMHTTPTransportError,
|
WinRM::WinRMHTTPTransportError,
|
||||||
WinRM::WinRMAuthorizationError,
|
WinRM::WinRMAuthorizationError,
|
||||||
|
WinRM::WinRMWSManFault,
|
||||||
Errno::EACCES,
|
Errno::EACCES,
|
||||||
Errno::EADDRINUSE,
|
Errno::EADDRINUSE,
|
||||||
Errno::ECONNREFUSED,
|
Errno::ECONNREFUSED,
|
||||||
|
|
|
@ -12,29 +12,32 @@ module VagrantPlugins
|
||||||
attr_accessor :keep_color
|
attr_accessor :keep_color
|
||||||
attr_accessor :name
|
attr_accessor :name
|
||||||
attr_accessor :powershell_args
|
attr_accessor :powershell_args
|
||||||
|
attr_accessor :elevated_interactive
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@args = UNSET_VALUE
|
@args = UNSET_VALUE
|
||||||
@inline = UNSET_VALUE
|
@inline = UNSET_VALUE
|
||||||
@path = UNSET_VALUE
|
@path = UNSET_VALUE
|
||||||
@upload_path = UNSET_VALUE
|
@upload_path = UNSET_VALUE
|
||||||
@privileged = UNSET_VALUE
|
@privileged = UNSET_VALUE
|
||||||
@binary = UNSET_VALUE
|
@binary = UNSET_VALUE
|
||||||
@keep_color = UNSET_VALUE
|
@keep_color = UNSET_VALUE
|
||||||
@name = UNSET_VALUE
|
@name = UNSET_VALUE
|
||||||
@powershell_args = UNSET_VALUE
|
@powershell_args = UNSET_VALUE
|
||||||
|
@elevated_interactive = UNSET_VALUE
|
||||||
end
|
end
|
||||||
|
|
||||||
def finalize!
|
def finalize!
|
||||||
@args = nil if @args == UNSET_VALUE
|
@args = nil if @args == UNSET_VALUE
|
||||||
@inline = nil if @inline == UNSET_VALUE
|
@inline = nil if @inline == UNSET_VALUE
|
||||||
@path = nil if @path == UNSET_VALUE
|
@path = nil if @path == UNSET_VALUE
|
||||||
@upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
|
@upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE
|
||||||
@privileged = true if @privileged == UNSET_VALUE
|
@privileged = true if @privileged == UNSET_VALUE
|
||||||
@binary = false if @binary == UNSET_VALUE
|
@binary = false if @binary == UNSET_VALUE
|
||||||
@keep_color = false if @keep_color == UNSET_VALUE
|
@keep_color = false if @keep_color == UNSET_VALUE
|
||||||
@name = nil if @name == UNSET_VALUE
|
@name = nil if @name == UNSET_VALUE
|
||||||
@powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE
|
@powershell_args = "-ExecutionPolicy Bypass" if @powershell_args == UNSET_VALUE
|
||||||
|
@elevated_interactive = false if @elevated_interactive == UNSET_VALUE
|
||||||
|
|
||||||
if @args && args_valid?
|
if @args && args_valid?
|
||||||
@args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s
|
@args = @args.is_a?(Array) ? @args.map { |a| a.to_s } : @args.to_s
|
||||||
|
@ -78,6 +81,10 @@ module VagrantPlugins
|
||||||
errors << I18n.t("vagrant.provisioners.shell.args_bad_type")
|
errors << I18n.t("vagrant.provisioners.shell.args_bad_type")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if elevated_interactive && !privileged
|
||||||
|
errors << I18n.t("vagrant.provisioners.shell.interactive_not_elevated")
|
||||||
|
end
|
||||||
|
|
||||||
{ "shell provisioner" => errors }
|
{ "shell provisioner" => errors }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ module VagrantPlugins
|
||||||
end
|
end
|
||||||
|
|
||||||
# Execute it with sudo
|
# Execute it with sudo
|
||||||
comm.sudo(command, elevated: config.privileged) do |type, data|
|
comm.sudo(command, { elevated: config.privileged, interactive: config.elevated_interactive }) do |type, data|
|
||||||
handle_comm(type, data)
|
handle_comm(type, data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2037,6 +2037,7 @@ en:
|
||||||
running: "Running: %{script}"
|
running: "Running: %{script}"
|
||||||
runningas: "Running: %{local} as %{remote}"
|
runningas: "Running: %{local} as %{remote}"
|
||||||
upload_path_not_set: "`upload_path` must be set for the shell provisioner."
|
upload_path_not_set: "`upload_path` must be set for the shell provisioner."
|
||||||
|
interactive_not_elevated: "To be interactive, it must also be privileged."
|
||||||
|
|
||||||
ansible:
|
ansible:
|
||||||
errors:
|
errors:
|
||||||
|
|
|
@ -93,6 +93,15 @@ describe VagrantPlugins::CommunicatorWinRM::Communicator do
|
||||||
expect(subject.execute("dir", { elevated: true })).to eq(0)
|
expect(subject.execute("dir", { elevated: true })).to eq(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "wraps command in elevated and interactive shell script when elevated and interactive are true" do
|
||||||
|
expect(shell).to receive(:upload).with(kind_of(String), "c:/tmp/vagrant-elevated-shell.ps1")
|
||||||
|
expect(shell).to receive(:powershell) do |cmd|
|
||||||
|
expect(cmd).to eq("powershell -executionpolicy bypass -file \"c:/tmp/vagrant-elevated-shell.ps1\" " +
|
||||||
|
"-username \"vagrant\" -password \"password\" -encoded_command \"ZABpAHIAOwAgAGUAeABpAHQAIAAkAEwAQQBTAFQARQBYAEkAVABDAE8ARABFAA==\"")
|
||||||
|
end.and_return({ exitcode: 0 })
|
||||||
|
expect(subject.execute("dir", { elevated: true, interactive: true })).to eq(0)
|
||||||
|
end
|
||||||
|
|
||||||
it "can use cmd shell" do
|
it "can use cmd shell" do
|
||||||
expect(shell).to receive(:cmd).with(kind_of(String)).and_return({ exitcode: 0 })
|
expect(shell).to receive(:cmd).with(kind_of(String)).and_return({ exitcode: 0 })
|
||||||
expect(subject.execute("dir", { shell: :cmd })).to eq(0)
|
expect(subject.execute("dir", { shell: :cmd })).to eq(0)
|
||||||
|
|
|
@ -85,6 +85,19 @@ describe "VagrantPlugins::Shell::Config" do
|
||||||
I18n.t("vagrant.provisioners.shell.args_bad_type")
|
I18n.t("vagrant.provisioners.shell.args_bad_type")
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "returns an error if elevated_interactive is true but privileged is false" do
|
||||||
|
subject.path = file_that_exists
|
||||||
|
subject.elevated_interactive = true
|
||||||
|
subject.privileged = false
|
||||||
|
subject.finalize!
|
||||||
|
|
||||||
|
result = subject.validate(machine)
|
||||||
|
|
||||||
|
expect(result["shell provisioner"]).to eq([
|
||||||
|
I18n.t("vagrant.provisioners.shell.interactive_not_elevated")
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'finalize!' do
|
describe 'finalize!' do
|
||||||
|
|
|
@ -45,8 +45,9 @@ The remainder of the available options are optional:
|
||||||
defaults to "true".
|
defaults to "true".
|
||||||
|
|
||||||
* `privileged` (boolean) - Specifies whether to execute the shell script
|
* `privileged` (boolean) - Specifies whether to execute the shell script
|
||||||
as a privileged user or not (`sudo`). By default this is "true". This has
|
as a privileged user or not (`sudo`). By default this is "true". Windows
|
||||||
no effect for Windows guests.
|
guests use a scheduled task to run as a true administrator without the
|
||||||
|
WinRM limitations.
|
||||||
|
|
||||||
* `upload_path` (string) - Is the remote path where the shell script will
|
* `upload_path` (string) - Is the remote path where the shell script will
|
||||||
be uploaded to. The script is uploaded as the SSH user over SCP, so this
|
be uploaded to. The script is uploaded as the SSH user over SCP, so this
|
||||||
|
@ -65,6 +66,11 @@ The remainder of the available options are optional:
|
||||||
* `powershell_args` (string) - Extra arguments to pass to `PowerShell`
|
* `powershell_args` (string) - Extra arguments to pass to `PowerShell`
|
||||||
if you're provisioning with PowerShell on Windows.
|
if you're provisioning with PowerShell on Windows.
|
||||||
|
|
||||||
|
* `elevated_interactive` (boolean) - Run an elevated script in interactive mode
|
||||||
|
on Windows. By default this is "false". Must also be `privileged`. Be sure to
|
||||||
|
enable auto-login for Windows as the user must be logged in for interactive
|
||||||
|
mode to work.
|
||||||
|
|
||||||
<a name="inline-scripts"></a>
|
<a name="inline-scripts"></a>
|
||||||
## Inline Scripts
|
## Inline Scripts
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue