Python script for MySQL Passwords Unreliable on first boot (rc.local)

Thomas Jollans thomas at jollybox.de
Mon Sep 13 17:32:22 CEST 2010


On Monday 13 September 2010, it occurred to cloudcontrol to exclaim:
> The script below works great when logged in as root and run from the
> command line, but when run at first boot using /etc/rc.local in Ubuntu
> 10.04, it fails about 25% of the time- the system root, mysql root and
> some mysql user passwords are set correctly, but one will fail with
> console log reporting standard mysql login error: "ERROR 1045 (28000):
> Access denied for user 'root' @ 'localhost' (using password: YES)"

It looks like MySQL itself isn't properly set up (yet) in those cases. But 
why/how?

Ubuntu uses upstart to boot up. I don't know much about how this works, but I 
understand it's dependency-based, and I expect it's parallel as well, where 
possible. This means that different init scripts could run in a different 
order sometimes, or even at the same time.

However, rc.local should really always be run last. It does not, by and of 
itself, specify any dependencies, but it could require anything. It would be 
ludicrous for Ubuntu to run rc.local before or at the same time as other 
scripts, such as the MySQL one. The only plausible explanation for this 
strange behaviour I can think of right now would be that either
 a) Upstart/Ubuntu is just plain stupid, or
 b) The MySQL init script spawns MySQL in the background and doesn't wait for
    it to start up correctly. This seams highly unlikely and atypical of MySQL
    init scripts, but maybe that's what's happening.
So this might be a bug in Ubuntu.

The way to fix this might simply be to detect the login error and retry, say, 
5 times with 1-second intervals. 

> 
> Is there something about running python scripts from init jobs that I
> should account for, such as an environment variable?
> 
> #!/usr/bin/env python
> 
> # Udates system & MySQL root passwords on first boot
> files = ['/home/ubuntu/passwords','/opt/data1/alfresco/extensions/
> extension/alfresco-global.properties','/opt/data/etc/mysql/
> debian.cnf','/home/ubuntu/duncil']
> userpasswords = {'root':'ROOTPASSWORD'}
> mysqlpasswords =
> {'root':'MYSQLPASSWORD','alfresco':'alfrescoPASSWORD','debian-sys-
> maint':'debian-sys-maintPASSWORD'}
> otherpasswords = ['OTHERPASSWORD']
> log = '/var/log/firstrun'
> 
> import random, string
> import crypt
> import re
> from subprocess import PIPE, Popen
> 
> def getsalt(chars = string.letters + string.digits):
>     # generate a random 2-character 'salt'
>     return random.choice(chars) + random.choice(chars)
> 
> def getpwd(chars = string.letters + string.digits, len = 12):
>     retval = "";
>     for i in range(0, len):
>     # generate 12 character alphanumeric password
>         retval += random.choice(chars)
> 
>     return retval
> 
> def replace_pass(filename):
>     handle = open(filename, 'r')
>     hbuf = handle.read()
>     handle.close()
>     for placeholder, password in pdict.iteritems():
>         hbuf = re.sub(placeholder, password, hbuf)
> 
>     try:
>         # Output file
>         handle = open(filename, 'w')
>         handle.write(hbuf)
>         handle.close()
>     except:
>         pass
>         #logh.write('failed to update ' + filename  + "\n")
>         #logh.write('maybe you don\'t have permision to write to it?
> \n')
> 
> logh = open(log, "a")
> logh.write("Starting...\n")
> # Generate passwords
> pdict = {}
> for user, placeholder in userpasswords.iteritems():
>     syspass = getpwd()
>     Popen(['usermod', '--password', crypt.crypt(syspass, getsalt()),
> user])
>     logh.write(placeholder + ": User " + user + " --> " + syspass +
> "\n")
>     pdict[placeholder] = syspass
> 
> # What's the MySQL Root password placeholder?
> mplace = mysqlpasswords['root']
> for user, placeholder in mysqlpasswords.iteritems():
>     mpass = getpwd()
>     if (("root" in mysqlpasswords) and (mysqlpasswords['root'] in
> pdict)):
>         mrootpass = pdict[mysqlpasswords['root']]
>     else:
>         mrootpass = ""
> 
>     Popen(['mysql', '-uroot', "--password=" + mrootpass, "-e", "UPDATE
> user SET Password = PASSWORD('" + mpass + "') WHERE User = '" + user +
> "';FLUSH PRIVILEGES;","mysql"])
>     logh.write(placeholder + ": MySQL " + user + " --> " + mpass +
> "\n")
>     pdict[placeholder] = mpass
> 
> for placeholder in otherpasswords:
>     opass = getpwd()
>     logh.write(placeholder + ": " + opass + "\n")
>     pdict[placeholder] = opass
> 
> # Update passwords
> for file in files:
>     logh.write("Replacing placeholders in " + file + "\n")
>     replace_pass(file)
> 
> logh.write("Finished\n")
> logh.close



More information about the Python-list mailing list