mirror of
https://github.com/certbot/certbot.git
synced 2026-06-06 07:12:54 -04:00
Instituted new file registration to make all config transactions atomic
This commit is contained in:
parent
a6d9b0f599
commit
c9f3d1d7fc
5 changed files with 85 additions and 32 deletions
12
trustify.py
12
trustify.py
|
|
@ -37,12 +37,16 @@ def main():
|
|||
elif o == "--server":
|
||||
server = a
|
||||
elif o == "--rollback":
|
||||
from trustify.client import configurator
|
||||
from trustify.client import configurator, logger
|
||||
logger.setLogger(logger.FileLogger(sys.stdout))
|
||||
logger.setLogLevel(logger.INFO)
|
||||
config = configurator.Configurator()
|
||||
config.recover_checkpoint(a)
|
||||
continue
|
||||
config.rollback_checkpoints(a)
|
||||
sys.exit(0)
|
||||
elif o == "--view-checkpoints":
|
||||
from trustify.client import configurator
|
||||
from trustify.client import configurator, logger
|
||||
logger.setLogger(logger.FileLogger(sys.stdout))
|
||||
logger.setLogLevel(logger.INFO)
|
||||
config = configurator.Configurator()
|
||||
config.display_checkpoints()
|
||||
sys.exit(0)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ OPTIONS_SSL_CONF = CONFIG_DIR + "options-ssl.conf"
|
|||
# Temporary file for challenge virtual hosts
|
||||
APACHE_CHALLENGE_CONF = CONFIG_DIR + "choc_sni_cert_challenge.conf"
|
||||
# Modified files intended to be reset (for challenges/tmp config changes)
|
||||
MODIFIED_FILES = WORK_DIR + "modified_files"
|
||||
ORPHAN_FILE = WORK_DIR + "orphans"
|
||||
|
||||
# Byte size of S and Nonce
|
||||
S_SIZE = 32
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ class Client(object):
|
|||
logger.setLogger(logger.NcursesLogger())
|
||||
logger.setLogLevel(logger.INFO)
|
||||
else:
|
||||
logger.setLogger(sys.stdout)
|
||||
logger.setLogger(logger.FileLogger(sys.stdout))
|
||||
logger.setLogLevel(logger.INFO)
|
||||
|
||||
def sanity_check_names(self, names):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import time
|
|||
import shutil
|
||||
import errno
|
||||
|
||||
from trustify.client.CONFIG import SERVER_ROOT, BACKUP_DIR, MODIFIED_FILES
|
||||
from trustify.client.CONFIG import SERVER_ROOT, BACKUP_DIR, ORPHAN_FILE
|
||||
#from CONFIG import SERVER_ROOT, BACKUP_DIR, MODIFIED_FILES, REWRITE_HTTPS_ARGS, CONFIG_DIR, WORK_DIR
|
||||
from trustify.client.CONFIG import REWRITE_HTTPS_ARGS, CONFIG_DIR, WORK_DIR
|
||||
from trustify.client.CONFIG import TEMP_CHECKPOINT_DIR, IN_PROGRESS_DIR
|
||||
|
|
@ -50,6 +50,7 @@ from trustify.client import logger, trustify_util
|
|||
# to the path to a recovery_specific file. This wouldn't clear out self.new_files
|
||||
# but would only be used in case of a crash... cleared every save, checked at
|
||||
# start...
|
||||
# STARTING WORK
|
||||
#
|
||||
# However, FILEPATHS and changes to files are transactional. They are copied
|
||||
# over before the updates are made to the existing files.
|
||||
|
|
@ -516,6 +517,10 @@ class Configurator(object):
|
|||
# Copy file
|
||||
ssl_fp = avail_fp + "-trustify-ssl"
|
||||
orig_file = open(avail_fp, 'r')
|
||||
|
||||
# First register the creation so that it is properly removed if
|
||||
# configuration is rolled back
|
||||
self.register_file_creation(ssl_fp)
|
||||
new_file = open(ssl_fp, 'w')
|
||||
new_file.write("<IfModule mod_ssl.c>\n")
|
||||
for line in orig_file:
|
||||
|
|
@ -523,8 +528,6 @@ class Configurator(object):
|
|||
new_file.write("</IfModule>\n")
|
||||
orig_file.close()
|
||||
new_file.close()
|
||||
# This is used for checkpoints
|
||||
self.new_files.append(ssl_fp)
|
||||
self.aug.load()
|
||||
# Delete the VH addresses because they may change here
|
||||
del nonssl_vhost.addrs[:]
|
||||
|
|
@ -683,16 +686,19 @@ LogLevel warn \n\
|
|||
# make sure servername doesn't exceed filename length restriction
|
||||
if ssl_vhost.names[0] < (255-23):
|
||||
redirect_filename = "trustify-redirect-" + ssl_vhost.names[0] + ".conf"
|
||||
|
||||
redirect_filepath = SERVER_ROOT + "sites-available/" + redirect_filename
|
||||
|
||||
# Register the new file that will be created
|
||||
# Note: always register the creation before writing to ensure file will
|
||||
# be removed in case of unexpected program exit
|
||||
self.register_file_creation(redirect_filepath)
|
||||
|
||||
# Write out file
|
||||
with open(SERVER_ROOT+"sites-available/"+redirect_filename, 'w') as f:
|
||||
with open(redirect_filepath, 'w') as f:
|
||||
f.write(redirect_file)
|
||||
logger.info("Created redirect file: " + redirect_filename)
|
||||
|
||||
# -- Now update data structures to reflect the change --
|
||||
# Make sure that checkpoint data will
|
||||
# remove the file if rollback is required
|
||||
self.new_files.append(redirect_filename)
|
||||
|
||||
self.aug.load()
|
||||
# Make a new vhost data structure and add it to the lists
|
||||
new_fp = SERVER_ROOT + "sites-available/" + redirect_filename
|
||||
|
|
@ -835,12 +841,11 @@ LogLevel warn \n\
|
|||
TODO: This function should number subdomains before the domain vhost
|
||||
"""
|
||||
if "/sites-available/" in vhost.file:
|
||||
index = vhost.file.rfind("/")
|
||||
enabled_path = "%ssites-enabled/%s" % (SERVER_ROOT, vhost.file[index:])
|
||||
enabled_path = "%ssites-enabled/%s" % (SERVER_ROOT, os.path.basename(vhost.file))
|
||||
self.register_file_creation(enabled_path)
|
||||
os.symlink(vhost.file, enabled_path)
|
||||
vhost.enabled = True
|
||||
self.save_notes += 'Enabled site %s\n' % vhost.file
|
||||
self.new_files.append(enabled_path)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -897,12 +902,18 @@ LogLevel warn \n\
|
|||
|
||||
def recovery_routine(self):
|
||||
"""
|
||||
Revert all previously modified files. First, any changes found in a
|
||||
Revert all previously modified files. First, remove any potentially
|
||||
orphaned files (those that did not make it to a checkpoint)
|
||||
Then any changes found in
|
||||
TEMP_CHECKPOINT_DIR are removed, then IN_PROGRESS changes are removed
|
||||
The order is important. IN_PROGRESS is unable to add files that are
|
||||
already added by a TEMP change. Thus TEMP must be rolled back first
|
||||
because that will be the 'latest' occurance of the file.
|
||||
"""
|
||||
# See if there were any orphaned files
|
||||
# (Files that were created but never found their way into a checkpoint)
|
||||
if self.__remove_contained_files(ORPHAN_FILE):
|
||||
self.aug.load()
|
||||
self.revert_challenge_config()
|
||||
if os.path.isdir(IN_PROGRESS_DIR):
|
||||
result = self.__recover_checkpoint(IN_PROGRESS_DIR)
|
||||
|
|
@ -916,6 +927,25 @@ LogLevel warn \n\
|
|||
# Need to reload configuration after these changes take effect
|
||||
self.aug.load()
|
||||
|
||||
def __remove_contained_files(self, file_list):
|
||||
"""
|
||||
Erase any files contained within the text file, file_list
|
||||
"""
|
||||
# Check to see that file exists to differentiate can't find file_list
|
||||
# and can't remove filepaths within file_list errors.
|
||||
if not os.isfile(file_list):
|
||||
return False
|
||||
try:
|
||||
with open(file_list, 'r') as f:
|
||||
filepaths = f.read().splitlines()
|
||||
for fp in filepaths:
|
||||
os.remove(fp)
|
||||
except IOError:
|
||||
logger.fatal("Unable to remove filepaths contained within %s" % file_list)
|
||||
sys.exit(41)
|
||||
|
||||
return True
|
||||
|
||||
def verify_dir_setup(self):
|
||||
'''
|
||||
Make sure that directories are setup with appropriate permissions
|
||||
|
|
@ -1097,6 +1127,9 @@ LogLevel warn \n\
|
|||
self.aug.set("/augeas/save", save_state)
|
||||
self.save_notes = ""
|
||||
del self.new_files[:]
|
||||
# Clear orphan file...
|
||||
# The orphans have been placed appropriately in a checkpoint
|
||||
open(ORPHAN_FILE, 'w').close()
|
||||
self.aug.save()
|
||||
|
||||
return True
|
||||
|
|
@ -1158,8 +1191,12 @@ LogLevel warn \n\
|
|||
nf_fd.write(filename + '\n')
|
||||
|
||||
def rollback_checkpoints(self, rollback = 1):
|
||||
try:
|
||||
rollback = int(rollback)
|
||||
except:
|
||||
logger.error("Rollback argument must be a positive integer")
|
||||
# Sanity check input
|
||||
if type(rollback) is not int or rollbackrollback < 1:
|
||||
if rollbackrollback < 1:
|
||||
logger.error("Rollback argument must be a positive integer")
|
||||
return
|
||||
|
||||
|
|
@ -1182,6 +1219,7 @@ LogLevel warn \n\
|
|||
def __recover_checkpoint(self, cp_dir):
|
||||
"""
|
||||
Recover a specific checkpoint provided by cp_dir
|
||||
Note: this function does not reload augeas.
|
||||
|
||||
returns: 0 success, 1 Unable to revert, -1 Unable to delete
|
||||
"""
|
||||
|
|
@ -1194,15 +1232,9 @@ LogLevel warn \n\
|
|||
# This file is required in all checkpoints.
|
||||
logger.error("Unable to recover files from %s" % cp_dir)
|
||||
return 1
|
||||
try:
|
||||
# Remove any newly added files if they exist
|
||||
with open(cp_dir + "/NEW_FILES") as f:
|
||||
filepaths = f.read().splitlines()
|
||||
for fp in filepaths:
|
||||
os.remove(fp)
|
||||
except:
|
||||
# This file is optional
|
||||
pass
|
||||
|
||||
# Remove any newly added files if they exist
|
||||
self.__remove_contained_files(cp_dir + "/NEW_FILES")
|
||||
|
||||
try:
|
||||
shutil.rmtree(cp_dir)
|
||||
|
|
@ -1279,6 +1311,23 @@ LogLevel warn \n\
|
|||
pass
|
||||
print ""
|
||||
|
||||
def register_file_creation(*files):
|
||||
"""
|
||||
This is used to register the creation of all files during Trustify
|
||||
execution. Call this method before writing to the file to make sure
|
||||
that the file will be cleaned up if the program exits unexpectedly.
|
||||
(Before a save occurs)
|
||||
"""
|
||||
try:
|
||||
with open(ORPHAN_FILE, 'a') as fd:
|
||||
for f in files:
|
||||
self.new_files.append(f)
|
||||
fd.write("%s\n" % f)
|
||||
except:
|
||||
logger.error("Unable to register file creation")
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
config = Configurator()
|
||||
logger.setLogger(logger.FileLogger(sys.stdout))
|
||||
|
|
@ -1310,7 +1359,7 @@ def main():
|
|||
config.aug.set("/files" + test_file + "/IfModule[1]/directive[2]/arg", "555")
|
||||
|
||||
#config.save_notes = "Added listen 431 for test"
|
||||
#config.new_files.append("/home/james/Desktop/new_file.txt")
|
||||
#config.register_file_creation("/home/james/Desktop/new_file.txt")
|
||||
#config.save("Testing Saves", False)
|
||||
#config.recover_checkpoint(1)
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ DocumentRoot " + CONFIG_DIR + "challenge_page/ \n \
|
|||
configText += "</IfModule> \n"
|
||||
|
||||
self.checkForApacheConfInclude(mainConfig)
|
||||
self.configurator.new_files.append(APACHE_CHALLENGE_CONF)
|
||||
self.configurator.register_file_creation(APACHE_CHALLENGE_CONF)
|
||||
newConf = open(APACHE_CHALLENGE_CONF, 'w')
|
||||
newConf.write(configText)
|
||||
newConf.close()
|
||||
|
|
@ -134,8 +134,8 @@ DocumentRoot " + CONFIG_DIR + "challenge_page/ \n \
|
|||
"""
|
||||
|
||||
self.updateCertConf(oid, ext)
|
||||
self.configurator.register_file_creation(self.getChocCertFile(nonce))
|
||||
subprocess.call(["openssl", "x509", "-req", "-days", "21", "-extfile", CHOC_CERT_CONF, "-extensions", "v3_ca", "-signkey", key, "-out", self.getChocCertFile(nonce), "-in", csr], stdout=open("/dev/null", 'w'), stderr=open("/dev/null", 'w'))
|
||||
self.configurator.new_files.append(self.getChocCertFile(nonce))
|
||||
|
||||
|
||||
def generateExtension(self, key, y):
|
||||
|
|
|
|||
Loading…
Reference in a new issue