pulumi-command icon indicating copy to clipboard operation
pulumi-command copied to clipboard

Add 'become' Support to `Command` resources for Privilege Escalation Without Passwordless Sudo

Open froazin opened this issue 1 year ago • 3 comments

Hello!

  • Vote on this issue by adding a 👍 reaction
  • If you want to implement this feature, comment to let us know (we'll work with you on design, scheduling, etc.)

Issue details

Currently, when using Pulumi's remote.Command and local.Command resources to execute commands that require elevated privileges (e.g., apt-get update), the user must be configured to allow passwordless privilege escalation. This setup can pose security concerns and adds complexity to the deployment process.

Affected area/feature

pulumi_command.remote.Command

Proposed Solution

Introduce a become parameter to the remote.Command resource, similar to Ansible's become directive. This parameter would enable users to specify privilege escalation within the Pulumi configuration, eliminating the need for passwordless sudo configuration on the remote host.

Example Usage (Python 🐍)

"""
Execute a remote command with privilege escalation built in.
"""

import pulumi_command as command

remote_command = command.remote.Command(
    "updatePackages",
    create="apt-get update",
    connection=command.remote.ConnectionArgs(
        host="hostname.local",
        user="your_user_name",
        password="your_password",
    ),
    become=True,
    becomeUser="root",
    becomeMethod="sudo",
    becomePassword="your_sudo_password",
)

froazin avatar Nov 21 '24 18:11 froazin

Thanks for the suggestion @froazin! I think it might be a little tricky to deliver this feature though; as it stands today, the command provider doesn't really know anything about the environment of the remote host; it's counting on the user to specify commands that work in the remote shell. (Eg. this becomes especially relevant when connecting to Windows hosts.)

As a workaround, you should be able to set the password in an environment variable use that to escalate. Something like:

import pulumi_command as command

remote_command = command.remote.Command(
    "updatePackages",
    create="echo \"$PASS\" sudo -S apt-get update",
    connection=command.remote.ConnectionArgs(
        host="hostname.local",
        user="your_user_name",
        password="your_password",
    ),
    environment: {
      PASS: "your_sudo_password"
    },
)

Of course the usual cautions should apply about using pulumi secrets to set that variable so it will be encrypted in all pulumi state & outputs.

mjeffryes avatar Nov 26 '24 15:11 mjeffryes

Ah that checks out @mjeffryes - thanks for the tip; that should do the trick!

I have just been using local command and calling the ansible-playbook cli. However, that requires a fair amount of boilerplate code if you want to consume the outputs.

I've been thinking about potentially using a dynamic resource provider with ansible runner; though, I wonder if a dedicated ansible package built on top of ansible_runner might be a better solution for more complex needs. Though the obvious drawback being that, while ansible can manage Windows hosts via winrm, it can only be run from Linux hosts.

froazin avatar Nov 26 '24 18:11 froazin

I probably don't have enough context to advise which would be the best solution for you, but I'll through one other option into the mix:

There's a open source provider for ansible: ansible/terraform-provider-ansible which you can use via our support for Any Terraform Provider

It looks like it would still involve parsing stdout/stderr from the ansible playbooks though.

mjeffryes avatar Nov 27 '24 00:11 mjeffryes