Question

Say I have a fabfile.py that looks like this:

def setup():                                
    pwd = getpass('mysql password: ')
    run('mysql -umoo -p%s something' % pwd)

The output of this is:

[host] run: mysql -umoo -pTheActualPassword

Is there a way to make the output look like this?

[host] run: mysql -umoo -p*******

Note: This is not a mysql question!

Was it helpful?

Solution

Rather than modifying / overriding Fabric, you could replace stdout (or any iostream) with a filter.

Here's an example of overriding stdout to censor a specific password. It gets the password from Fabric's env.password variable, set by the -I argument. Note that you could do the same thing with a regular expression, so that you wouldn't have to specify the password in the filter.

I should also mention, this isn't the most efficient code in the world, but if you're using fabric you're likely gluing a couple things together and care more about manageability than speed.

#!/usr/bin/python

import sys
import string
from fabric.api import *
from fabric.tasks import *
from fabric.contrib import *

class StreamFilter(object):

    def __init__(self, filter, stream):
        self.stream = stream
        self.filter = filter

    def write(self,data):
        data = data.replace(self.filter, '[[TOP SECRET]]')
        self.stream.write(data)
        self.stream.flush()

    def flush(self):
        self.stream.flush()

@task
def can_you_see_the_password():
    sys.stdout = StreamFilter(env.password, sys.stdout)
    print 'Hello there'
    print 'My password is %s' % env.password 

When run:

fab -I can_you_see_the_password
Initial value for env.password:

this will produce:

Hello there
My password is [[TOP SECRET]]

OTHER TIPS

It may be better to put the password in the user's ~/.my.cnf under the [client] section. This way you don't have to put the password in the python file.

[client]
password=TheActualPassword

When you use the Fabric command run, Fabric isn't aware of whether or not the command you are running contains a plain-text password or not. Without modifying/overriding the Fabric source code, I don't think you can get the output that you want where the command being run is shown but the password is replaced with asterisks.

You could, however, change the Fabric output level, either for the entire Fabric script or a portion, so that the command being run is not displayed. While this will hide the password, the downside is that you wouldn't see the command at all.

Take a look at the Fabric documentation on Managing Output.

Write a shell script that invokes the command in question with the appropriate password, but without echoing that password. You can have the shell script lookup the password from a more secure location than from your .py files.

Then have fabric call the shell script instead.

This solves both the problem of having fabric not display the password and making sure you don't have credentials in your source code.

from fabric.api import run, settings
with settings(prompts={'Enter password: ': mysql_password}):
    run("mysql -u {} -p -e {}".format(mysql_user,mysql_query))

or if no prompt available:

from fabric.api import run, hide
with hide('output','running','warnings'):
   run("mycommand --password {}".format(my_password))
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top