How to Mask Command Line Output from the Python run() Method
Recently, We were tasked with creating automated deployment for our Python Django project. For our purposes, this involved creating python modules for programatically logging onto servers and carrying out all the necessary deployment tasks. We decided to use Fabric to make this work. We encountered difficulty, however, when using the run() method to execute commands in remote environments.
The problem is, with every command that Fabric executes on the remote machine, the command itself is always echoed to standard-out when the run() method executes, displaying (in plain text!) any password arguments you may be passing to the command being executed.
We worked around this by “monkey-patching” stdout itself with a wrapper class that checks specifically for the password provided, and replacing any output to standard out matching the password with something else (like “****”).
The wrapper class looks something like this:
def __init__(self, stdout, mask):
self.stdout = stdout
self.mask = mask
def __getattr__(self, attr_name):
return getattr(self.stdout, attr_name)
def write(self, text):
if text.find(self.mask) > -1:
Then, when you want to execute the command whose output you wish to mask, you simply use the StdoutWrapper class like so:
import getpass.getpass, sys
config.username = prompt('Enter Username:')
config.passwd = getpass('Enter Password:')
config.app_path = '/path/to/your/app'
config.svn_url = 'http://some.svn.checkout/'
out = StdoutWrapper(sys.stdout, config.passwd)
sys.stdout = out
run("svn revert -R $(app_path")
run("svn switch --username $(username) --password $(svn_password) $(svn_url) $(app_path)")
sys.stdout = sys.__stdout__
Obviously, this is a less-than-ideal solution; it accomplishes what we want, but having to resort to temporarily monkey-patching standard out itself seems like overkill. Has anyone else out there found a more preferable solution for suppressing plain-text password output while using Fabric for Python?
By: Brett McClelland