From mboxrd@z Thu Jan 1 00:00:00 1970 From: rmccabe@sourceware.org Date: 26 Jun 2006 20:01:42 -0000 Subject: [Cluster-devel] conga/luci/utils luci_admin Message-ID: <20060626200142.30244.qmail@sourceware.org> List-Id: To: cluster-devel.redhat.com MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit CVSROOT: /cvs/cluster Module name: conga Changes by: rmccabe at sourceware.org 2006-06-26 20:01:40 Modified files: luci/utils : luci_admin Log message: fix certificate backup and restore, and some other cleanups. Patches: http://sourceware.org/cgi-bin/cvsweb.cgi/conga/luci/utils/luci_admin.diff?cvsroot=cluster&r1=1.18&r2=1.19 --- conga/luci/utils/luci_admin 2006/06/21 23:06:28 1.18 +++ conga/luci/utils/luci_admin 2006/06/26 20:01:40 1.19 @@ -19,16 +19,34 @@ LUCI_USER='luci' LUCI_GROUP='luci' -SSL_PRIVKEY_PATH='/var/lib/luci/var/certs/privkey.pem' -SSL_PUBKEY_PATH='/var/lib/luci/var/certs/cacert.pem' -SSL_KEYCONFIG_PATH='/var/lib/luci/var/certs/cacert.config' LUCI_BACKUP_DIR='/var/lib/luci/var' +LUCI_CERT_DIR='/var/lib/luci/var/certs/' LUCI_BACKUP_PATH=LUCI_BACKUP_DIR + '/luci_backup.xml' LUCI_DB_PATH='/var/lib/luci/var/Data.fs' LUCI_TEMP='/var/lib/luci/var/temp/' INITUSER_FILE_PATH = '/var/lib/luci/inituser' +SSL_PRIVKEY_NAME = 'privkey.pem' +SSL_PUBKEY_NAME = 'cacert.pem' +SSL_HTTPS_PRIVKEY_NAME = 'https.key.pem' +SSL_HTTPS_PUBKEY_NAME = 'https.pem' +SSL_KEYCONFIG_NAME = 'cacert.config' + +SSL_PRIVKEY_PATH = LUCI_CERT_DIR + SSL_PRIVKEY_NAME +SSL_PUBKEY_PATH = LUCI_CERT_DIR + SSL_HTTPS_PRIVKEY_NAME +SSL_HTTPS_PRIVKEY_PATH = LUCI_CERT_DIR + SSL_HTTPS_PRIVKEY_NAME +SSL_HTTPS_PUBKEY_PATH = LUCI_CERT_DIR + SSL_HTTPS_PUBKEY_NAME +SSL_KEYCONFIG_PATH = LUCI_CERT_DIR + SSL_KEYCONFIG_NAME + +ssl_key_data = [ + { 'id': SSL_PRIVKEY_PATH, 'name': SSL_PRIVKEY_NAME, 'type': 'private', 'mode': 0600 }, + { 'id': SSL_HTTPS_PRIVKEY_PATH, 'name': SSL_HTTPS_PRIVKEY_NAME, 'type': 'private', 'mode': 0600 }, + { 'id': SSL_PUBKEY_PATH, 'name': SSL_PUBKEY_NAME, 'type': 'public', 'mode': 0644 }, + { 'id': SSL_HTTPS_PUBKEY_PATH, 'name': SSL_HTTPS_PUBKEY_NAME, 'type': 'public', 'mode': 0644 }, + { 'id': SSL_KEYCONFIG_PATH, 'name': SSL_KEYCONFIG_NAME, 'type': 'config', 'mode': 0644 } +] + null = file(os.devnull, 'rwb+', 0) orig_stderr = sys.stderr @@ -56,6 +74,60 @@ except: return -1 +def luci_restore_certs(certList): + if not certList or len(certList) < 1: + sys.stderr.write('Your backup file contains no certificate data. Please check that your backup file is not corrupt.\n') + return -1 + + certList = certList[0].getElementsByTagName('certificate') + if not certList or len(certList) < 1: + sys.stderr.write('Your backup file contains no certificate data. Please check that your backup file is not corrupt.\n') + return -1 + try: + luci = pwd.getpwnam(LUCI_USER)[2:4] + if not luci or len(luci) != 2: + raise + except: + sys.stderr.write('Cannot find the \"' + LUCI_USER + '\" user.\n') + return -1 + + for c in certList: + path = c.getAttribute('name') + if not path: + sys.stderr.write('Missing \"name\" field for certificate.\n') + return -1 + path = LUCI_CERT_DIR + str(path) + + mode = c.getAttribute('mode') + if not mode: + mode = 0600 + else: + mode = int(mode, 8) + + data = c.firstChild + if not data or not data.wholeText: + sys.stderr.write('\"' + path + '\" has no certificate data.') + return -1 + + # Because .prettyprint() was called to write the backup.. + data = data.wholeText.strip() + if len(data) < 1: + sys.stderr.write('\"' + path + '\" has no certificate data.') + return -1 + data = str(data) + + try: + f = file(path, 'wb+') + except: + sys.stderr.write('Unable to create \" ' + path + '\" for writing.\n') + return -1 + + os.chmod(path, mode) + f.write(data + '\n') + os.chown(path, luci[0], luci[1]) + f.close() + return None + def luci_restore(argv): import ZODB from ZODB.FileStorage import FileStorage @@ -349,6 +421,16 @@ db.pack() db.close() fs.close() + + certList = node.getElementsByTagName('certificateList') + if not certList or len(certList) < 1: + sys.stderr.write('No certificate data was found.\n') + return -1 + + if luci_restore_certs(certList): + sys.stderr.write('An error occurred while restoring certificate data.\n') + return -1 + return 0 # This function's ability to work is dependent @@ -539,69 +621,43 @@ doc.appendChild(luciData) dataNode = dataToXML(doc, backup, 'backupData') - try: - certfile = file(SSL_PRIVKEY_PATH, 'rb') - output = certfile.read() - - # should be at least some length greater than one - # TODO: find out what the min length of a valid keyfile is. - if len(output) < 1: - raise - - certNode = doc.createElement('certificate') - certNode.setAttribute('name', SSL_PRIVKEY_PATH) - certNode.setAttribute('data', output) - dataNode.appendChild(certNode.cloneNode(True)) - certfile.close() - except False: - sys.stderr.write('Unable to read ' + SSL_PRIVKEY_PATH + '\n') - return None - - try: - certfile = file(SSL_PUBKEY_PATH, 'rb') - output = certfile.read() + certList = doc.createElement('certificateList') + for i in ssl_key_data: + try: + certfile = file(i['id'], 'rb') + output = certfile.read() + certfile.close() - # should be at least some length greater than one - # TODO: find out what the min length of a valid keyfile is. - if len(output) < 1: - raise + if len(output) < 1: + raise + except: + sys.stderr.write('Unable to read \"' + i['id'] + '\"\n') + # An error backing up anything other than the config + # is fatal. + if i['type'] != 'config': + return None certNode = doc.createElement('certificate') - certNode.setAttribute('name', SSL_PUBKEY_PATH) - certNode.setAttribute('data', output) - dataNode.appendChild(certNode.cloneNode(True)) - certfile.close() - except: - sys.stderr.write('Unable to read ' + SSL_PUBKEY_PATH + '\n') - return None - - try: - certfile = file(SSL_KEYCONFIG_PATH, 'rb') - output = certfile.read() - - # should be at least some length greater than one - # TODO: find out what the min length of a valid key conf is. - if len(output) < 1: - raise - - certNode = document.createElement('certificateConfig') - certNode.setAttribute('name', SSL_KEYCONFIG_PATH) - certNode.setAttribute('data', output) - dataNode.appendChild(certNode.cloneNode(TRUE)) - certfile.close() - except: - sys.stderr.write('Unable to read ' + SSL_KEYCONFIG_PATH + '\n') + certNode.setAttribute('id', i['id']) + certNode.setAttribute('name', i['name']) + certNode.setAttribute('type', i['type']) + certNode.setAttribute('mode', str(oct(i['mode']))) + textNode = doc.createTextNode('\n' + output) + certNode.appendChild(textNode) + certList.appendChild(certNode) + dataNode.appendChild(certList.cloneNode(True)) luciData.appendChild(dataNode) + return doc def _execWithCaptureErrorStatus(command, argv, searchPath = 0, root = '/', stdin = 0, catchfd = 1, catcherrfd = 2, closefd = -1): if not os.access (root + command, os.X_OK): raise RuntimeError, command + " can not be run" - + (read, write) = os.pipe() (read_err,write_err) = os.pipe() - + childpid = os.fork() if (not childpid): # child @@ -613,7 +669,7 @@ os.dup2(write, catchfd) os.close(write) os.close(read) - + if isinstance(catcherrfd, tuple): for fd in catcherrfd: os.dup2(write_err, fd) @@ -621,23 +677,23 @@ os.dup2(write_err, catcherrfd) os.close(write_err) os.close(read_err) - + if closefd != -1: os.close(closefd) - + if stdin: os.dup2(stdin, 0) os.close(stdin) - + if (searchPath): os.execvp(command, argv) else: os.execv(command, argv) # will never come here - + os.close(write) os.close(write_err) - + rc = "" rc_err = "" in_list = [read, read_err] @@ -654,21 +710,21 @@ if s == '': in_list.remove(read_err) rc_err = rc_err + s - + os.close(read) os.close(read_err) - + status = -1 try: (pid, status) = os.waitpid(childpid, 0) except OSError, (errno, msg): print __name__, "waitpid:", msg - + if os.WIFEXITED(status): status = os.WEXITSTATUS(status) else: status = -1 - + return (rc, rc_err, status) @@ -688,31 +744,31 @@ command = '/usr/bin/openssl' args = [command, 'genrsa', '-out', SSL_PRIVKEY_PATH, '2048'] _execWithCaptureErrorStatus(command, args) - + # /usr/bin/openssl req -new -x509 -key /var/lib/luci/var/certs/privkey.pem -out /var/lib/luci/var/certs/cacert.pem -days 1095 -config /var/lib/luci/var/certs/cacert.config command = '/usr/bin/openssl' args = [command, 'req', '-new', '-x509', '-key', SSL_PRIVKEY_PATH, '-out', SSL_PUBKEY_PATH, '-days', '1095', '-config', SSL_KEYCONFIG_PATH] _execWithCaptureErrorStatus(command, args) - + # /bin/chown -R zope:zope /var/lib/luci/var/certs/* command = '/bin/chown' args = [command, LUCI_USER + ':' + LUCI_GROUP, SSL_PRIVKEY_PATH, SSL_PUBKEY_PATH] _execWithCaptureErrorStatus(command, args) - + # /bin/chmod 644 /var/lib/luci/var/certs/* command = '/bin/chmod' args = [command, '644', SSL_PUBKEY_PATH] o, e, s = _execWithCaptureErrorStatus(command, args) if s != 0: return False - + # /bin/chmod 400 /var/lib/luci/var/certs/privkey.pem command = '/bin/chmod' args = [command, '600', SSL_PRIVKEY_PATH] o, e, s = _execWithCaptureErrorStatus(command, args) if s != 0: return False - + return True @@ -734,7 +790,7 @@ print 'Restart the Luci server for changes to take effect' print 'eg. service luci restart' print - return + return @@ -753,14 +809,14 @@ if generate_ssl_certs() == False: print 'failed. exiting ...' sys.exit(1) - + print print 'Creating \'admin\' user' save_password('admin', generate_password()) print 'Luci server has been successfully initialized' - + restart_message() - + return @@ -771,14 +827,14 @@ print argv[0] + ' init' sys.exit(1) pass - + print 'Reseting admin password' print save_password('admin', generate_password()) print 'admin password has been successfully reset' - + restart_message() - + return @@ -788,18 +844,25 @@ print 'To initialize it, execute' print argv[0] + ' init' sys.exit(1) - + print 'Backing up the Luci server...' try: os.umask(077) except: pass doc = luci_backup(argv[2:]) - if not doc: - sys.stderr.write('The Luci backup failed.\n') - sys.exit(1) try: + luci = pwd.getpwnam(LUCI_USER)[2:4] + os.chown(LUCI_DB_PATH, luci[0], luci[1]) + os.chmod(LUCI_DB_PATH, 0600) + except: + sys.stderr.write('Unable to change ownership of the Luci database back to user \"' + LUCI_USER + '\"\n') + + try: + # The LUCI_BACKUP_DIR must not be world-writable + # as the code below is obviously not safe against + # races. stat = os.stat(LUCI_BACKUP_PATH) trynum = 1 basename = '/luci_backup-' @@ -816,46 +879,41 @@ except OSError, e: #if e[0] == 2: pass - - try: - f = file(LUCI_BACKUP_PATH, 'w') - except: - sys.stderr.write('Unable to open \"' + LUCI_BACKUP_PATH + '\" to write backup.\n') - return -1 - - f.write(doc.toprettyxml()) - f.flush() - f.close() - - print 'Backup was successful. The backup file is',LUCI_BACKUP_PATH try: - luci = pwd.getpwnam(LUCI_USER)[2:4] - os.chown(LUCI_DB_PATH, luci[0], luci[1]) - os.chmod(LUCI_DB_PATH, 0600) + f = file(LUCI_BACKUP_PATH, 'wb+') except: - sys.stderr.write('Unable to change ownership of the Luci database back to user \"' + LUCI_USER + '\"\n') + sys.stderr.write('Unable to open \"' + LUCI_BACKUP_PATH + '\" to write backup.\n') + return -1 try: os.chmod(LUCI_BACKUP_PATH, 0600) except OSError, e: print "An error occurred while making",LUCI_BACKUP_PATH,"read-only:",e print "Please check that this file is not world-readable." + if not doc: + sys.stderr.write('The Luci backup failed.\n') + sys.exit(1) + f.write(doc.toprettyxml()) + f.flush() + f.close() def restore(argv): print 'Restoring the Luci server...' - + + try: os.umask(077) + except: pass + # for reasons that make no sense, zope/plone will fall # on its face if www/ok.gif and images/version.gif don't # exist and have@least something in them. try: os.makedirs(LUCI_TEMP + 'www') os.makedirs(LUCI_TEMP + 'images') - except:pass + except: pass try: - os.chdir(LUCI_TEMP) f = file(LUCI_TEMP + 'www/ok.gif', 'w') f.write('luci\n') f.close() @@ -864,12 +922,18 @@ f.write('luci\n') f.close() except: - os.unlink(LUCI_TEMP + 'www/ok.gif') - os.unlink(LUCI_TEMP + 'images/version.gif') - os.removedirs(LUCI_TEMP) + try: + os.unlink(LUCI_TEMP + 'www/ok.gif') + os.unlink(LUCI_TEMP + 'images/version.gif') + os.rmdir(LUCI_TEMP + 'www') + os.rmdir(LUCI_TEMP + 'images') + os.rmdir(LUCI_TEMP) + except: pass print 'Unable to initialize restore.' sys.exit(1) + os.chdir(LUCI_TEMP) + ret = True if luci_restore(argv[2:]): print 'The Luci restore failed. Try reinstalling Luci, then restoring again.' @@ -878,6 +942,15 @@ print 'Restore was successful.' restart_message() + os.chdir(LUCI_BACKUP_DIR) + try: + os.unlink(LUCI_TEMP + 'www/ok.gif') + os.unlink(LUCI_TEMP + 'images/version.gif') + os.rmdir(LUCI_TEMP + 'www') + os.rmdir(LUCI_TEMP + 'images') + os.rmdir(LUCI_TEMP) + except: pass + try: luci = pwd.getpwnam(LUCI_USER)[2:4] os.chown(LUCI_DB_PATH, luci[0], luci[1]) @@ -885,12 +958,6 @@ except: sys.stderr.write('Unable to change ownership of the Luci database back to user \"' + LUCI_USER + '\"\n') - try: - os.unlink(LUCI_TEMP + 'www/ok.gif') - os.unlink(LUCI_TEMP + 'images/version.gif') - os.removedirs(LUCI_TEMP) - except: pass - return ret @@ -904,7 +971,7 @@ print '\tpassword: reset admin password' print '\thelp: this help message' print - + def main(argv): @@ -912,16 +979,16 @@ luci_help(argv) sys.exit(1) pass - - + + # only root can modify Luci server if os.getuid() != 0: print 'Only root can modify Luci server.' print 'Try again with root privileges.' sys.exit(2) pass - - + + if 'init' in argv: init(argv) elif 'backup' in argv: @@ -938,7 +1005,7 @@ luci_help(argv) sys.exit(1) - + # If called from the command line if __name__ == '__main__': main(sys.argv)