Escalating privilege with Bolt
By default, Bolt connects to, and executes on, remote systems as the same user. Sometimes, connection and execution need to be done as separate users. For example, you might need to install a package as the root user on a system that doesn't allow incoming connections as the root user. Bolt has several configuration options for setting which user to execute as, how to escalate to that user, and how to run commands as that user.
Limitations
Bolt will only use the run-as
user if:
The target shell is Bash
The transport is Local or SSH
The configured
run-as
user is different than the connectinguser
Escalating privilege must be generalized
Limiting privilege escalation permission to certain commands will not work well when running Bolt tasks, scripts, and some plans. Bolt often uses a temporary directory to copy content to remote systems and execute, which prevents Bolt's remote commands from being predictable. Furthermore, Bolt is an automation tool, and many organizations enforce specific security policies to prevent the kind of automated interaction that Bolt is performing. As a result, you will need to allow arbitrary script execution for any users who need to use Bolt to run tasks or scripts. We recommend either having a dedicated user to run Bolt on target systems and managing access to that user, or using Puppet Enterprise to limit which tasks a user can run on which targets.
Configuring run-as
You can use privilege escalation from the command-line, or set it up using Bolt's configuration files.
Command-line options
--run-as USER
User to run as using privilege escalation.
--sudo-password PASSWORD
Password for privilege escalation.
--sudo-password-prompt
Prompt for user to input escalation password.
--sudo-executable EXEC
Experimental. Specify an executable for running as another user.
Configuration file options
You can configure escalation privilege in your inventory.yaml
or
bolt-defaults.yaml
files. For more information on which file to use, see
Configuring Bolt.
run-as
Type: String
run-as-command
Type: Array
sudo-executable
Type: String
sudo-password
Type: String
How run-as
works
Each time Bolt executes, it builds a command string to run on the remote
system. The command is built from many configuration options, including
interpreters, task input method, and the run-as
configuration. When run-as
is set, the following is prepended to the command:
<sudo-executable> -S -H -u <run-as> -p '[sudo] Bolt needs to run as another user, password:'
The default sudo-executable
is sudo
. If you're running a task that
reads parameters from environment variables, -E
will be appended.
Note: The command is shell escaped, so each value is interpreted literally. This means you can't set sudo command-line options in the
run-as
configuration. Use therun-as-command
option to specify your own sudo command and command-line options.
You can replace this command wholesale by specifying run-as-command
, which
will prepend the following to the command being run:
<run-as-command> <run-as>
This enables using any executable and command-line options to change users. Keep in mind that:
The
run-as-command
is non-interactive. If you setrun-as-command
and the system prompts for a password, Bolt will error instead of supplying the password.Bolt always appends the specified
run-as
user to the specifiedrun-as-command
, which can be limiting for some escalating commands.Bolt will not use the
run-as-command
option ifrun-as
is not set.
Escalating privilege over WinRM
The WinRM transport does not support run-as
. This is because WinRM operates
with a session level token and has no functionality to change to another user
after authentication. Once you connect with a user, you can only perform
actions that you have permission to perform with that user. Instead of
switching users, you can connect with one user, and then provide a secondary
user's credentials to a program to perform another action. The behavior of providing
credentials to a program is not part of WinRM.
For example, to get the processes running on a Windows target, you could
connect to the target with WinRM using Invoke-BoltScript -Script
.\myscript.ps1 -Targets winrm://mytarget -User userA -Password passwordA
and
then use a Powershell script to pass credentials to the Get-WmiObject
command:
$Cred = New-Object System.Management.Automation.PsCredential('userB',$passwordB)
Get-WmiObject -Class Win32_BIOS -Computer SERVER1 -Credential $Cred
If you want to securely pass credentials into a Powershell script, you have two options:
Convert your script into a task and use sensitive task parameters: This is the best option if you need your secret obfuscated in logs. You can either pass the parameters as static values to a task, or compute them per target and use a plan to pass them to the task.
Use a remote task: If your secrets are unique to each target and you don't want to write a Bolt plan, you can use a remote task. This option requires you to manage the connection to the target yourself, rather than letting Bolt manage the connection.
If you're not sure where to start, we recommend using a task with sensitive parameters. If your secrets are unique to targets, you must also use an inventory plugin and a Bolt plan. This method is demonstrated in the example below.
If your secrets are the same for every target, you can pass sensitive parameters to a task as static values. If your secrets are unique to each target, or the secrets need to be computed or fetched, set the secrets as data on the target in the inventory. For the purposes of escalating as a secondary user, you can set arbitrary variables on targets in the inventory which you can then access from a Bolt plan.
You can set variables on the target object in your inventory, either statically as plaintext in the inventory file or dynamically using inventory plugins. You can then access variables on the target object in a plan, or using a remote task.
Remote tasks run on localhost and pass the entire target object as JSON to the
task as a metaparameter _target
. You can write a task to get the secret, and
then connect to the remote target and use the secret there. Put another way, if
you're using a remote task, you have to manage the connection to the remote
target in your task rather than letting Bolt manage it for you. You should
only use a remote task if you are comfortable managing connections in a script
and don't want to write a plan, or are unable to connect directly to the target
using Bolt.
Example
This example uses a task with sensitive parameters, together with a Bolt plan and the PKCS7 inventory plugin.
The inventory file below uses the PKCS7 plugin to set the
secondary_user_password
variable on two groups of targets:
# inventory.yaml
---
groups:
- name: databases
targets:
- mywindows.com
- mywindows2.com
vars:
secondary_user_pw:
_plugin: pkcs7
encrypted_value: |
ENC[PKCS7, <ENCRYPTED_DATA>]
- name: load_balancers
targets:
- mywindows3.com
- mywindows4.com
vars:
secondary_user_pw:
_plugin: pkcs7
encrypted_value: |
ENC[PKCS7, <OTHER_ENCRYPTED_DATA>]
The example::plan
plan collects the variables from the targets and passes them
to the example::task
task as sensitive parameters:
# This is the example plan that we use to get variables from targets and pass them to the task
plan example::plan(
TargetSpec $targets
) {
# This gets all the data for the targets from the inventory and creates
# Target objects.
$target_objects = get_targets($targets)
run_task_with('example::task', $targets) |$target| {
# You can also pass static values here.
{ 'secondarypassword' => $target.vars['secondary_user_pw'],
'message' => 'Good bye' }
}
}
The task's metadata.json
file defines secondarypassword
as a sensitive parameter:
{
"input_method": "powershell",
"parameters": {
"secondarypassword": {
"type": "String",
"description": "The password for the user to run 'GetWMI' as",
"sensitive": true
},
"message": {
"type": "String",
"description": "The message to print when finished"
}
}
}
The task recieves the sensitive parameters from the plan:
[CmdletBinding()]
param(
[Parameter(Mandatory = $True)]
[string]
$Secondarypassword
[Parameter(Mandatory = $True)]
[string]
$Message
)
$Pw = $Secondarypassword | ConvertTo-SecureString -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PsCredential('bolt', $Pw)
Get-WmiObject -Class Win32_BIOS -Computer SERVER1 -Credential $Cred
Write-Output $Message
📖 Related information