[cleanup] Remove legacy migrations and code snippets which won't be needed anymore on buster

This commit is contained in:
Alexandre Aubin 2020-03-05 22:52:19 +01:00
parent 04894b1b87
commit 60dd1fc848
18 changed files with 1 additions and 1157 deletions

View file

@ -406,54 +406,8 @@
"mail_unavailable": "This e-mail address is reserved and shall be automatically allocated to the very first user",
"main_domain_change_failed": "Unable to change the main domain",
"main_domain_changed": "The main domain has been changed",
"migrate_tsig_end": "Migration to HMAC-SHA-512 finished",
"migrate_tsig_failed": "Could not migrate the DynDNS domain '{domain}' to HMAC-SHA-512, rolling back. Error: {error_code}, {error}",
"migrate_tsig_start": "Insufficiently secure key algorithm detected for TSIG signature of the domain '{domain}', initiating migration to the more secure HMAC-SHA-512",
"migrate_tsig_wait": "Waiting three minutes for the DynDNS server to take the new key into account...",
"migrate_tsig_wait_2": "2 min...",
"migrate_tsig_wait_3": "1 min...",
"migrate_tsig_wait_4": "30 seconds...",
"migrate_tsig_not_needed": "You do not appear to use a DynDNS domain, so no migration is needed.",
"migration_description_0001_change_cert_group_to_sslcert": "Change certificates group permissions from 'metronome' to 'ssl-cert'",
"migration_description_0002_migrate_to_tsig_sha256": "Improve security of DynDNS TSIG updates by using SHA-512 instead of MD5",
"migration_description_0003_migrate_to_stretch": "Upgrade the system to Debian Stretch and YunoHost 3.0",
"migration_description_0004_php5_to_php7_pools": "Reconfigure the PHP pools to use PHP 7 instead of 5",
"migration_description_0005_postgresql_9p4_to_9p6": "Migrate databases from PostgreSQL 9.4 to 9.6",
"migration_description_0006_sync_admin_and_root_passwords": "Synchronize admin and root passwords",
"migration_description_0007_ssh_conf_managed_by_yunohost_step1": "Let the SSH configuration be managed by YunoHost (step 1, automatic)",
"migration_description_0008_ssh_conf_managed_by_yunohost_step2": "Let the SSH configuration be managed by YunoHost (step 2, manual)",
"migration_description_0009_decouple_regenconf_from_services": "Decouple the regen-conf mechanism from services",
"migration_description_0010_migrate_to_apps_json": "Remove deprecated apps catalogs and use the new unified 'apps.json' list instead (outdated, replaced by migration 13)",
"migration_description_0011_setup_group_permission": "Set up user groups and permissions for apps and services",
"migration_description_0012_postgresql_password_to_md5_authentication": "Force PostgreSQL authentication to use MD5 for local connections",
"migration_description_0013_futureproof_apps_catalog_system": "Migrate to the new future-proof apps catalog system",
"migration_description_0014_remove_app_status_json": "Remove legacy status.json app files",
"migration_description_0011_setup_group_permission": "Set up user group and set up permission for apps and services",
"migration_description_0015_migrate_to_buster": "Upgrade the system to Debian Buster and YunoHost 4.x",
"migration_0003_start": "Starting migration to Stretch. The logs will be available in {logfile}.",
"migration_0003_patching_sources_list": "Patching the sources.lists...",
"migration_0003_main_upgrade": "Starting main upgrade...",
"migration_0003_fail2ban_upgrade": "Starting the Fail2Ban upgrade...",
"migration_0003_restoring_origin_nginx_conf": "Your file /etc/nginx/nginx.conf was edited somehow. The migration is going to reset it to its original state first… The previous file will be available as {backup_dest}.",
"migration_0003_yunohost_upgrade": "Starting the YunoHost package upgrade… The migration will end, but the actual upgrade will happen immediately afterwards. After the operation is complete, you might have to log in to the webadmin page again.",
"migration_0003_not_jessie": "The current Debian distribution is not Jessie!",
"migration_0003_system_not_fully_up_to_date": "Your system is not fully up-to-date. Please perform a regular upgrade before running the migration to Stretch.",
"migration_0003_still_on_jessie_after_main_upgrade": "Something went wrong during the main upgrade: Is the system still on Jessie‽ To investigate the issue, please look at {log}:s…",
"migration_0003_general_warning": "Please note that this migration is a delicate operation. The YunoHost team did its best to review and test it, but the migration might still break parts of the system or its apps.\n\nTherefore, it is recommended to:\n - Perform a backup of any critical data or app. More info on https://yunohost.org/backup;\n - Be patient after launching the migration: Depending on your Internet connection and hardware, it might take up to a few hours for everything to upgrade.\n\nAdditionally, the port for SMTP, used by external e-mail clients (like Thunderbird or K9-Mail) was changed from 465 (SSL/TLS) to 587 (STARTTLS). The old port (465) will automatically be closed, and the new port (587) will be opened in the firewall. You and your users *will* have to adapt the configuration of your e-mail clients accordingly.",
"migration_0003_problematic_apps_warning": "Please note that the following possibly problematic installed apps were detected. It looks like those were not installed from an app catalog, or are not flagged as 'working'. Consequently, it cannot be guaranteed that they will still work after the upgrade: {problematic_apps}",
"migration_0003_modified_files": "Please note that the following files were found to be manually modified and might be overwritten following the upgrade: {manually_modified_files}",
"migration_0005_postgresql_94_not_installed": "PostgreSQL was not installed on your system. Nothing to do.",
"migration_0005_postgresql_96_not_installed": "PostgreSQL 9.4 is installed, but not postgresql 9.6‽ Something weird might have happened on your system :(…",
"migration_0005_not_enough_space": "Make sufficient space available in {path} to run the migration.",
"migration_0006_disclaimer": "YunoHost now expects the admin and root passwords to be synchronized. This migration replaces your root password with the admin password.",
"migration_0007_cancelled": "Could not improve the way your SSH configuration is managed.",
"migration_0007_cannot_restart": "SSH can't be restarted after trying to cancel migration number 6.",
"migration_0008_general_disclaimer": "To improve the security of your server, it is recommended to let YunoHost manage the SSH configuration. Your current SSH setup differs from the recommendation. If you let YunoHost reconfigure it, the way you connect to your server through SSH will change thusly:",
"migration_0008_port": "• You will have to connect using port 22 instead of your current custom SSH port. Feel free to reconfigure it;",
"migration_0008_root": "• You will not be able to connect as root through SSH. Instead you should use the admin user;",
"migration_0008_dsa": "• The DSA key will be turned off. Hence, you might need to invalidate a spooky warning from your SSH client, and recheck the fingerprint of your server;",
"migration_0008_warning": "If you understand those warnings and want YunoHost to override your current configuration, run the migration. Otherwise, you can also skip the migration, though it is not recommended.",
"migration_0008_no_warning": "Overriding your SSH configuration should be safe, though this cannot be promised! Run the migration to override it. Otherwise, you can also skip the migration, though it is not recommended.",
"migration_0009_not_needed": "This migration already happened somehow... (?) Skipping.",
"migration_0011_backup_before_migration": "Creating a backup of LDAP database and apps settings prior to the actual migration.",
"migration_0011_can_not_backup_before_migration": "The backup of the system could not be completed before the migration failed. Error: {error:s}",
"migration_0011_create_group": "Creating a group for each user…",

View file

@ -2683,12 +2683,6 @@ def _read_apps_catalog_list():
Read the json corresponding to the list of apps catalogs
"""
# Legacy code - can be removed after moving to buster (if the migration got merged before buster)
if os.path.exists('/etc/yunohost/appslists.json'):
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("futureproof_apps_catalog_system")
migration.run()
try:
list_ = read_yaml(APPS_CATALOG_CONF)
# Support the case where file exists but is empty

View file

@ -1,15 +0,0 @@
import subprocess
import glob
from yunohost.tools import Migration
from moulinette.utils.filesystem import chown
class MyMigration(Migration):
"Change certificates group permissions from 'metronome' to 'ssl-cert'"
all_certificate_files = glob.glob("/etc/yunohost/certs/*/*.pem")
def run(self):
for filename in self.all_certificate_files:
chown(filename, uid="root", gid="ssl-cert")

View file

@ -1,86 +0,0 @@
import glob
import os
import requests
import base64
import time
import json
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.dyndns import _guess_current_dyndns_domain
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate Dyndns stuff from MD5 TSIG to SHA512 TSIG"
def run(self, dyn_host="dyndns.yunohost.org", domain=None, private_key_path=None):
if domain is None or private_key_path is None:
try:
(domain, private_key_path) = _guess_current_dyndns_domain(dyn_host)
assert "+157" in private_key_path
except (YunohostError, AssertionError):
logger.info(m18n.n("migrate_tsig_not_needed"))
return
logger.info(m18n.n('migrate_tsig_start', domain=domain))
public_key_path = private_key_path.rsplit(".private", 1)[0] + ".key"
public_key_md5 = open(public_key_path).read().strip().split(' ')[-1]
os.system('cd /etc/yunohost/dyndns && '
'dnssec-keygen -a hmac-sha512 -b 512 -r /dev/urandom -n USER %s' % domain)
os.system('chmod 600 /etc/yunohost/dyndns/*.key /etc/yunohost/dyndns/*.private')
# +165 means that this file store a hmac-sha512 key
new_key_path = glob.glob('/etc/yunohost/dyndns/*+165*.key')[0]
public_key_sha512 = open(new_key_path).read().strip().split(' ', 6)[-1]
try:
r = requests.put('https://%s/migrate_key_to_sha512/' % (dyn_host),
data={
'public_key_md5': base64.b64encode(public_key_md5),
'public_key_sha512': base64.b64encode(public_key_sha512),
}, timeout=30)
except requests.ConnectionError:
raise YunohostError('no_internet_connection')
if r.status_code != 201:
try:
error = json.loads(r.text)['error']
except Exception:
# failed to decode json
error = r.text
import traceback
from StringIO import StringIO
stack = StringIO()
traceback.print_stack(file=stack)
logger.error(stack.getvalue())
# Migration didn't succeed, so we rollback and raise an exception
os.system("mv /etc/yunohost/dyndns/*+165* /tmp")
raise YunohostError('migrate_tsig_failed', domain=domain,
error_code=str(r.status_code), error=error)
# remove old certificates
os.system("mv /etc/yunohost/dyndns/*+157* /tmp")
# sleep to wait for dyndns cache invalidation
logger.info(m18n.n('migrate_tsig_wait'))
time.sleep(60)
logger.info(m18n.n('migrate_tsig_wait_2'))
time.sleep(60)
logger.info(m18n.n('migrate_tsig_wait_3'))
time.sleep(30)
logger.info(m18n.n('migrate_tsig_wait_4'))
time.sleep(30)
logger.info(m18n.n('migrate_tsig_end'))
return

View file

@ -1,379 +0,0 @@
import glob
import os
from shutil import copy2
from moulinette import m18n, msettings
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import check_output, call_async_output
from moulinette.utils.filesystem import read_file
from yunohost.tools import Migration
from yunohost.app import unstable_apps
from yunohost.service import _run_service_command
from yunohost.regenconf import (manually_modified_files,
manually_modified_files_compared_to_debian_default)
from yunohost.utils.filesystem import free_space_in_directory
from yunohost.utils.packages import get_ynh_package_version
from yunohost.utils.network import get_network_interfaces
from yunohost.firewall import firewall_allow, firewall_disallow
logger = getActionLogger('yunohost.migration')
YUNOHOST_PACKAGES = ["yunohost", "yunohost-admin", "moulinette", "ssowat"]
class MyMigration(Migration):
"Upgrade the system to Debian Stretch and Yunohost 3.0"
mode = "manual"
def run(self):
self.logfile = "/var/log/yunohost/{}.log".format(self.name)
self.check_assertions()
logger.info(m18n.n("migration_0003_start", logfile=self.logfile))
# Preparing the upgrade
self.restore_original_nginx_conf_if_needed()
logger.info(m18n.n("migration_0003_patching_sources_list"))
self.patch_apt_sources_list()
self.backup_files_to_keep()
self.apt_update()
apps_packages = self.get_apps_equivs_packages()
self.unhold(["metronome"])
self.hold(YUNOHOST_PACKAGES + apps_packages + ["fail2ban"])
# Main dist-upgrade
logger.info(m18n.n("migration_0003_main_upgrade"))
_run_service_command("stop", "mysql")
self.apt_dist_upgrade(conf_flags=["old", "miss", "def"])
_run_service_command("start", "mysql")
if self.debian_major_version() == 8:
raise YunohostError("migration_0003_still_on_jessie_after_main_upgrade", log=self.logfile)
# Specific upgrade for fail2ban...
logger.info(m18n.n("migration_0003_fail2ban_upgrade"))
self.unhold(["fail2ban"])
# Don't move this if folder already exists. If it does, we probably are
# running this script a 2nd, 3rd, ... time but /etc/fail2ban will
# be re-created only for the first dist-upgrade of fail2ban
if not os.path.exists("/etc/fail2ban.old"):
os.system("mv /etc/fail2ban /etc/fail2ban.old")
self.apt_dist_upgrade(conf_flags=["new", "miss", "def"])
_run_service_command("restart", "fail2ban")
self.disable_predicable_interface_names()
# Clean the mess
os.system("apt autoremove --assume-yes")
os.system("apt clean --assume-yes")
# We moved to port 587 for SMTP
# https://busylog.net/smtp-tls-ssl-25-465-587/
firewall_allow("Both", 587)
firewall_disallow("Both", 465)
# Upgrade yunohost packages
logger.info(m18n.n("migration_0003_yunohost_upgrade"))
self.restore_files_to_keep()
self.unhold(YUNOHOST_PACKAGES + apps_packages)
self.upgrade_yunohost_packages()
def debian_major_version(self):
# The python module "platform" and lsb_release are not reliable because
# on some setup, they still return Release=8 even after upgrading to
# stretch ... (Apparently this is related to OVH overriding some stuff
# with /etc/lsb-release for instance -_-)
# Instead, we rely on /etc/os-release which should be the raw info from
# the distribution...
return int(check_output("grep VERSION_ID /etc/os-release | head -n 1 | tr '\"' ' ' | cut -d ' ' -f2"))
def yunohost_major_version(self):
return int(get_ynh_package_version("yunohost")["version"].split('.')[0])
def check_assertions(self):
# Be on jessie (8.x) and yunohost 2.x
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 9.x but yunohost package
# would still be in 2.x...
if not self.debian_major_version() == 8 \
and not self.yunohost_major_version() == 2:
raise YunohostError("migration_0003_not_jessie")
# Have > 1 Go free space on /var/ ?
if free_space_in_directory("/var/") / (1024**3) < 1.0:
raise YunohostError("There is not enough free space in /var/ to run the migration. You need at least 1GB free space")
# Check system is up to date
# (but we don't if 'stretch' is already in the sources.list ...
# which means maybe a previous upgrade crashed and we're re-running it)
if " stretch " not in read_file("/etc/apt/sources.list"):
self.apt_update()
apt_list_upgradable = check_output("apt list --upgradable -a")
if "upgradable" in apt_list_upgradable:
raise YunohostError("migration_0003_system_not_fully_up_to_date")
@property
def disclaimer(self):
# Avoid having a super long disclaimer + uncessary check if we ain't
# on jessie / yunohost 2.x anymore
# NB : we do both check to cover situations where the upgrade crashed
# in the middle and debian version could be >= 9.x but yunohost package
# would still be in 2.x...
if not self.debian_major_version() == 8 \
and not self.yunohost_major_version() == 2:
return None
# Get list of problematic apps ? I.e. not official or community+working
problematic_apps = unstable_apps()
problematic_apps = "".join(["\n - " + app for app in problematic_apps])
# Manually modified files ? (c.f. yunohost service regen-conf)
modified_files = manually_modified_files()
# We also have a specific check for nginx.conf which some people
# modified and needs to be upgraded...
if "/etc/nginx/nginx.conf" in manually_modified_files_compared_to_debian_default():
modified_files.append("/etc/nginx/nginx.conf")
modified_files = "".join(["\n - " + f for f in modified_files])
message = m18n.n("migration_0003_general_warning")
if problematic_apps:
message += "\n\n" + m18n.n("migration_0003_problematic_apps_warning", problematic_apps=problematic_apps)
if modified_files:
message += "\n\n" + m18n.n("migration_0003_modified_files", manually_modified_files=modified_files)
return message
def patch_apt_sources_list(self):
sources_list = glob.glob("/etc/apt/sources.list.d/*.list")
sources_list.append("/etc/apt/sources.list")
# This :
# - replace single 'jessie' occurence by 'stretch'
# - comments lines containing "backports"
# - replace 'jessie/updates' by 'strech/updates' (or same with a -)
# - switch yunohost's repo to forge
for f in sources_list:
command = "sed -i -e 's@ jessie @ stretch @g' " \
"-e '/backports/ s@^#*@#@' " \
"-e 's@ jessie/updates @ stretch/updates @g' " \
"-e 's@ jessie-updates @ stretch-updates @g' " \
"-e 's@repo.yunohost@forge.yunohost@g' " \
"{}".format(f)
os.system(command)
def get_apps_equivs_packages(self):
command = "dpkg --get-selections" \
" | grep -v deinstall" \
" | awk '{print $1}'" \
" | { grep 'ynh-deps$' || true; }"
output = check_output(command).strip()
return output.split('\n') if output else []
def hold(self, packages):
for package in packages:
os.system("apt-mark hold {}".format(package))
def unhold(self, packages):
for package in packages:
os.system("apt-mark unhold {}".format(package))
def apt_update(self):
command = "apt-get update"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
os.system(command)
def upgrade_yunohost_packages(self):
#
# Here we use a dirty hack to run a command after the current
# "yunohost tools migrations migrate", because the upgrade of
# yunohost will also trigger another "yunohost tools migrations migrate"
# (also the upgrade of the package, if executed from the webadmin, is
# likely to kill/restart the api which is in turn likely to kill this
# command before it ends...)
#
MOULINETTE_LOCK = "/var/run/moulinette_yunohost.lock"
upgrade_command = ""
upgrade_command += " DEBIAN_FRONTEND=noninteractive"
upgrade_command += " APT_LISTCHANGES_FRONTEND=none"
upgrade_command += " apt-get install"
upgrade_command += " --assume-yes "
upgrade_command += " ".join(YUNOHOST_PACKAGES)
# We also install php-zip and php7.0-acpu to fix an issue with
# nextcloud and kanboard that need it when on stretch.
upgrade_command += " php-zip php7.0-apcu"
upgrade_command += " 2>&1 | tee -a {}".format(self.logfile)
wait_until_end_of_yunohost_command = "(while [ -f {} ]; do sleep 2; done)".format(MOULINETTE_LOCK)
command = "({} && {}; echo 'Migration complete!') &".format(wait_until_end_of_yunohost_command,
upgrade_command)
logger.debug("Running command :\n{}".format(command))
os.system(command)
def apt_dist_upgrade(self, conf_flags):
# Make apt-get happy
os.system("echo 'libc6 libraries/restart-without-asking boolean true' | debconf-set-selections")
# Don't send an email to root about the postgresql migration. It should be handled automatically after.
os.system("echo 'postgresql-common postgresql-common/obsolete-major seen true' | debconf-set-selections")
command = ""
command += " DEBIAN_FRONTEND=noninteractive"
command += " APT_LISTCHANGES_FRONTEND=none"
command += " apt-get"
command += " --fix-broken --show-upgraded --assume-yes"
for conf_flag in conf_flags:
command += ' -o Dpkg::Options::="--force-conf{}"'.format(conf_flag)
command += " dist-upgrade"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
is_api = msettings.get('interface') == 'api'
if is_api:
callbacks = (
lambda l: logger.info(l.rstrip()),
lambda l: logger.warning(l.rstrip()),
)
call_async_output(command, callbacks, shell=True)
else:
# We do this when running from the cli to have the output of the
# command showing in the terminal, since 'info' channel is only
# enabled if the user explicitly add --verbose ...
os.system(command)
# Those are files that should be kept and restored before the final switch
# to yunohost 3.x... They end up being modified by the various dist-upgrades
# (or need to be taken out momentarily), which then blocks the regen-conf
# as they are flagged as "manually modified"...
files_to_keep = [
"/etc/mysql/my.cnf",
"/etc/nslcd.conf",
"/etc/postfix/master.cf",
"/etc/fail2ban/filter.d/yunohost.conf"
]
def backup_files_to_keep(self):
logger.debug("Backuping specific files to keep ...")
# Create tmp directory if it does not exists
tmp_dir = os.path.join("/tmp/", self.name)
if not os.path.exists(tmp_dir):
os.mkdir(tmp_dir, 0o700)
for f in self.files_to_keep:
dest_file = f.strip('/').replace("/", "_")
# If the file is already there, we might be re-running the migration
# because it previously crashed. Hence we keep the existing file.
if os.path.exists(os.path.join(tmp_dir, dest_file)):
continue
copy2(f, os.path.join(tmp_dir, dest_file))
def restore_files_to_keep(self):
logger.debug("Restoring specific files to keep ...")
tmp_dir = os.path.join("/tmp/", self.name)
for f in self.files_to_keep:
dest_file = f.strip('/').replace("/", "_")
copy2(os.path.join(tmp_dir, dest_file), f)
# On some setups, /etc/nginx/nginx.conf got edited. But this file needs
# to be upgraded because of the way the new module system works for nginx.
# (in particular, having the line that include the modules at the top)
#
# So here, if it got edited, we force the restore of the original conf
# *before* starting the actual upgrade...
#
# An alternative strategy that was attempted was to hold the nginx-common
# package and have a specific upgrade for it like for fail2ban, but that
# leads to apt complaining about not being able to upgrade for shitty
# reasons >.>
def restore_original_nginx_conf_if_needed(self):
if "/etc/nginx/nginx.conf" not in manually_modified_files_compared_to_debian_default():
return
if not os.path.exists("/etc/nginx/nginx.conf"):
return
# If stretch is in the sources.list, we already started migrating on
# stretch so we don't re-do this
if " stretch " in read_file("/etc/apt/sources.list"):
return
backup_dest = "/home/yunohost.conf/backup/nginx.conf.bkp_before_stretch"
logger.warning(m18n.n("migration_0003_restoring_origin_nginx_conf",
backup_dest=backup_dest))
os.system("mv /etc/nginx/nginx.conf %s" % backup_dest)
command = ""
command += " DEBIAN_FRONTEND=noninteractive"
command += " APT_LISTCHANGES_FRONTEND=none"
command += " apt-get"
command += " --fix-broken --show-upgraded --assume-yes"
command += ' -o Dpkg::Options::="--force-confmiss"'
command += " install --reinstall"
command += " nginx-common"
logger.debug("Running apt command :\n{}".format(command))
command += " 2>&1 | tee -a {}".format(self.logfile)
is_api = msettings.get('interface') == 'api'
if is_api:
callbacks = (
lambda l: logger.info(l.rstrip()),
lambda l: logger.warning(l.rstrip()),
)
call_async_output(command, callbacks, shell=True)
else:
# We do this when running from the cli to have the output of the
# command showing in the terminal, since 'info' channel is only
# enabled if the user explicitly add --verbose ...
os.system(command)
def disable_predicable_interface_names(self):
# Try to see if currently used interface names are predictable ones or not...
# If we ain't using "eth0" or "wlan0", assume we are using predictable interface
# names and therefore they shouldnt be disabled
network_interfaces = get_network_interfaces().keys()
if "eth0" not in network_interfaces and "wlan0" not in network_interfaces:
return
interfaces_config = read_file("/etc/network/interfaces")
if "eth0" not in interfaces_config and "wlan0" not in interfaces_config:
return
# Disable predictive interface names
# c.f. https://unix.stackexchange.com/a/338730
os.system("ln -s /dev/null /etc/systemd/network/99-default.link")

View file

@ -1,99 +0,0 @@
import os
import glob
from shutil import copy2
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.service import _run_service_command
logger = getActionLogger('yunohost.migration')
PHP5_POOLS = "/etc/php5/fpm/pool.d"
PHP7_POOLS = "/etc/php/7.0/fpm/pool.d"
PHP5_SOCKETS_PREFIX = "/var/run/php5-fpm"
PHP7_SOCKETS_PREFIX = "/run/php/php7.0-fpm"
MIGRATION_COMMENT = "; YunoHost note : this file was automatically moved from {}".format(PHP5_POOLS)
class MyMigration(Migration):
"Migrate php5-fpm 'pool' conf files to php7 stuff"
dependencies = ["migrate_to_stretch"]
def run(self):
# Get list of php5 pool files
php5_pool_files = glob.glob("{}/*.conf".format(PHP5_POOLS))
# Keep only basenames
php5_pool_files = [os.path.basename(f) for f in php5_pool_files]
# Ignore the "www.conf" (default stuff, probably don't want to touch it ?)
php5_pool_files = [f for f in php5_pool_files if f != "www.conf"]
for f in php5_pool_files:
# Copy the files to the php7 pool
src = "{}/{}".format(PHP5_POOLS, f)
dest = "{}/{}".format(PHP7_POOLS, f)
copy2(src, dest)
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, dest)
os.system(c)
# Also add a comment that it was automatically moved from php5
# (for human traceability and backward migration)
c = "sed -i '1i {}' {}".format(MIGRATION_COMMENT, dest)
os.system(c)
# Some old comments starting with '#' instead of ';' are not
# compatible in php7
c = "sed -i 's/^#/;#/g' {}".format(dest)
os.system(c)
# Reload/restart the php pools
_run_service_command("restart", "php7.0-fpm")
_run_service_command("enable", "php7.0-fpm")
os.system("systemctl stop php5-fpm")
os.system("systemctl disable php5-fpm")
os.system("rm /etc/logrotate.d/php5-fpm") # We remove this otherwise the logrotate cron will be unhappy
# Get list of nginx conf file
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf")
for f in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP5_SOCKETS_PREFIX, PHP7_SOCKETS_PREFIX, f)
os.system(c)
# Reload nginx
_run_service_command("reload", "nginx")
def backward(self):
# Get list of php7 pool files
php7_pool_files = glob.glob("{}/*.conf".format(PHP7_POOLS))
# Keep only files which have the migration comment
php7_pool_files = [f for f in php7_pool_files if open(f).readline().strip() == MIGRATION_COMMENT]
# Delete those files
for f in php7_pool_files:
os.remove(f)
# Reload/restart the php pools
_run_service_command("stop", "php7.0-fpm")
os.system("systemctl start php5-fpm")
# Get list of nginx conf file
nginx_conf_files = glob.glob("/etc/nginx/conf.d/*.d/*.conf")
for f in nginx_conf_files:
# Replace the socket prefix if it's found
c = "sed -i -e 's@{}@{}@g' {}".format(PHP7_SOCKETS_PREFIX, PHP5_SOCKETS_PREFIX, f)
os.system(c)
# Reload nginx
_run_service_command("reload", "nginx")

View file

@ -1,41 +0,0 @@
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
from yunohost.utils.filesystem import free_space_in_directory, space_used_by_directory
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate DBs from Postgresql 9.4 to 9.6 after migrating to Stretch"
dependencies = ["migrate_to_stretch"]
def run(self):
if not self.package_is_installed("postgresql-9.4"):
logger.warning(m18n.n("migration_0005_postgresql_94_not_installed"))
return
if not self.package_is_installed("postgresql-9.6"):
raise YunohostError("migration_0005_postgresql_96_not_installed")
if not space_used_by_directory("/var/lib/postgresql/9.4") > free_space_in_directory("/var/lib/postgresql"):
raise YunohostError("migration_0005_not_enough_space", path="/var/lib/postgresql/")
subprocess.check_call("service postgresql stop", shell=True)
subprocess.check_call("pg_dropcluster --stop 9.6 main", shell=True)
subprocess.check_call("pg_upgradecluster -m upgrade 9.4 main", shell=True)
subprocess.check_call("pg_dropcluster --stop 9.4 main", shell=True)
subprocess.check_call("service postgresql start", shell=True)
def package_is_installed(self, package_name):
p = subprocess.Popen("dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name), shell=True)
p.communicate()
return p.returncode == 0

View file

@ -1,78 +0,0 @@
import spwd
import crypt
import random
import string
import subprocess
from moulinette import m18n
from yunohost.utils.error import YunohostError
from moulinette.utils.log import getActionLogger
from moulinette.utils.process import run_commands, check_output
from moulinette.utils.filesystem import append_to_file
from moulinette.authenticators.ldap import Authenticator
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
SMALL_PWD_LIST = ["yunohost", "olinuxino", "olinux", "raspberry", "admin", "root", "test", "rpi"]
class MyMigration(Migration):
"Synchronize admin and root passwords"
def run(self):
new_hash = self._get_admin_hash()
self._replace_root_hash(new_hash)
logger.info(m18n.n("root_password_replaced_by_admin_password"))
@property
def mode(self):
# If the root password is still a "default" value,
# then this is an emergency and migration shall
# be applied automatically
#
# Otherwise, as playing with root password is touchy,
# we set this as a manual migration.
return "auto" if self._is_root_pwd_listed(SMALL_PWD_LIST) else "manual"
@property
def disclaimer(self):
if self._is_root_pwd_listed(SMALL_PWD_LIST):
return None
return m18n.n("migration_0006_disclaimer")
def _get_admin_hash(self):
"""
Fetch the admin hash from the LDAP db using slapcat
"""
admin_hash = check_output("slapcat \
| grep 'dn: cn=admin,dc=yunohost,dc=org' -A20 \
| grep userPassword -A2 \
| tr -d '\n ' \
| tr ':' ' ' \
| awk '{print $2}' \
| base64 -d \
| sed 's/{CRYPT}//g'")
return admin_hash
def _replace_root_hash(self, new_hash):
hash_root = spwd.getspnam("root").sp_pwd
with open('/etc/shadow', 'r') as before_file:
before = before_file.read()
with open('/etc/shadow', 'w') as after_file:
after_file.write(before.replace("root:" + hash_root,
"root:" + new_hash))
def _is_root_pwd_listed(self, pwd_list):
hash_root = spwd.getspnam("root").sp_pwd
for password in pwd_list:
if hash_root == crypt.crypt(password, hash_root):
return True
return False

View file

@ -1,70 +0,0 @@
import os
import re
from shutil import copyfile
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import mkdir, rm
from yunohost.tools import Migration
from yunohost.service import _run_service_command
from yunohost.regenconf import regen_conf
from yunohost.settings import settings_set
from yunohost.utils.error import YunohostError
logger = getActionLogger('yunohost.migration')
SSHD_CONF = '/etc/ssh/sshd_config'
class MyMigration(Migration):
"""
This is the first step of a couple of migrations that ensure SSH conf is
managed by YunoHost (even if the "from_script" flag is present, which was
previously preventing it from being managed by YunoHost)
The goal of this first (automatic) migration is to make sure that the
sshd_config is managed by the regen-conf mechanism.
If the from_script flag exists, then we keep the current SSH conf such that it
will appear as "manually modified" to the regenconf.
In step 2 (manual), the admin will be able to choose wether or not to actually
use the recommended configuration, with an appropriate disclaimer.
"""
def run(self):
# Check if deprecated DSA Host Key is in config
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
dsa = False
for line in open(SSHD_CONF):
if re.match(dsa_rgx, line) is not None:
dsa = True
break
if dsa:
settings_set("service.ssh.allow_deprecated_dsa_hostkey", True)
# Here, we make it so that /etc/ssh/sshd_config is managed
# by the regen conf (in particular in the case where the
# from_script flag is present - in which case it was *not*
# managed by the regenconf)
# But because we can't be sure the user wants to use the
# recommended conf, we backup then restore the /etc/ssh/sshd_config
# right after the regenconf, such that it will appear as
# "manually modified".
if os.path.exists('/etc/yunohost/from_script'):
rm('/etc/yunohost/from_script')
copyfile(SSHD_CONF, '/etc/ssh/sshd_config.bkp')
regen_conf(names=['ssh'], force=True)
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
# Restart ssh and rollback if it failed
if not _run_service_command('restart', 'ssh'):
# We don't rollback completely but it should be enough
copyfile('/etc/ssh/sshd_config.bkp', SSHD_CONF)
if not _run_service_command('restart', 'ssh'):
raise YunohostError("migration_0007_cannot_restart")
else:
raise YunohostError("migration_0007_cancelled")

View file

@ -1,105 +0,0 @@
import os
import re
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import chown
from yunohost.tools import Migration
from yunohost.regenconf import _get_conf_hashes, _calculate_hash
from yunohost.regenconf import regen_conf
from yunohost.settings import settings_set, settings_get
from yunohost.utils.error import YunohostError
from yunohost.backup import ARCHIVES_PATH
logger = getActionLogger('yunohost.migration')
SSHD_CONF = '/etc/ssh/sshd_config'
class MyMigration(Migration):
"""
In this second step, the admin is asked if it's okay to use
the recommended SSH configuration - which also implies
disabling deprecated DSA key.
This has important implications in the way the user may connect
to its server (key change, and a spooky warning might be given
by SSH later)
A disclaimer explaining the various things to be aware of is
shown - and the user may also choose to skip this migration.
"""
dependencies = ["ssh_conf_managed_by_yunohost_step1"]
def run(self):
settings_set("service.ssh.allow_deprecated_dsa_hostkey", False)
regen_conf(names=['ssh'], force=True)
# Update local archives folder permissions, so that
# admin can scp archives out of the server
if os.path.isdir(ARCHIVES_PATH):
chown(ARCHIVES_PATH, uid="admin", gid="root")
@property
def mode(self):
# If the conf is already up to date
# and no DSA key is used, then we're good to go
# and the migration can be done automatically
# (basically nothing shall change)
ynh_hash = _get_conf_hashes('ssh').get(SSHD_CONF, None)
current_hash = _calculate_hash(SSHD_CONF)
dsa = settings_get("service.ssh.allow_deprecated_dsa_hostkey")
if ynh_hash == current_hash and not dsa:
return "auto"
return "manual"
@property
def disclaimer(self):
if self.mode == "auto":
return None
# Detect key things to be aware of before enabling the
# recommended configuration
dsa_key_enabled = False
ports = []
root_login = []
port_rgx = r'^[ \t]*Port[ \t]+(\d+)[ \t]*(?:#.*)?$'
root_rgx = r'^[ \t]*PermitRootLogin[ \t]([^# \t]*)[ \t]*(?:#.*)?$'
dsa_rgx = r'^[ \t]*HostKey[ \t]+/etc/ssh/ssh_host_dsa_key[ \t]*(?:#.*)?$'
for line in open(SSHD_CONF):
ports = ports + re.findall(port_rgx, line)
root_login = root_login + re.findall(root_rgx, line)
if not dsa_key_enabled and re.match(dsa_rgx, line) is not None:
dsa_key_enabled = True
custom_port = ports != ['22'] and ports != []
root_login_enabled = root_login and root_login[-1] != 'no'
# Build message
message = m18n.n("migration_0008_general_disclaimer")
if custom_port:
message += "\n\n" + m18n.n("migration_0008_port")
if root_login_enabled:
message += "\n\n" + m18n.n("migration_0008_root")
if dsa_key_enabled:
message += "\n\n" + m18n.n("migration_0008_dsa")
if custom_port or root_login_enabled or dsa_key_enabled:
message += "\n\n" + m18n.n("migration_0008_warning")
else:
message += "\n\n" + m18n.n("migration_0008_no_warning")
return message

View file

@ -1,39 +0,0 @@
import os
from moulinette import m18n
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_file
from yunohost.service import _get_services, _save_services
from yunohost.regenconf import _update_conf_hashes, REGEN_CONF_FILE
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"""
Decouple the regen conf mechanism from the concept of services
"""
def run(self):
if "conffiles" not in read_file("/etc/yunohost/services.yml") \
or os.path.exists(REGEN_CONF_FILE):
logger.warning(m18n.n("migration_0009_not_needed"))
return
# For all services
services = _get_services()
for service, infos in services.items():
# If there are some conffiles (file hashes)
if "conffiles" in infos.keys():
# Save them using the new regen conf thingy
_update_conf_hashes(service, infos["conffiles"])
# And delete the old conffile key from the service infos
del services[service]["conffiles"]
# (Actually save the modification of services)
_save_services(services)

View file

@ -1,13 +0,0 @@
from moulinette.utils.log import getActionLogger
from yunohost.tools import Migration
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"Migrate from official.json to apps.json (outdated, replaced by migration 13)"
def run(self):
logger.info("This migration is oudated and doesn't do anything anymore. The migration 13 will handle this instead.")
pass

View file

@ -1,16 +0,0 @@
import glob
import re
from yunohost.tools import Migration
from moulinette.utils.filesystem import read_file, write_to_file
class MyMigration(Migration):
"Force authentication in md5 for local connexions"
all_hba_files = glob.glob("/etc/postgresql/*/*/pg_hba.conf")
def run(self):
for filename in self.all_hba_files:
pg_hba_in = read_file(filename)
write_to_file(filename, re.sub(r"local(\s*)all(\s*)all(\s*)password", "local\\1all\\2all\\3md5", pg_hba_in))

View file

@ -1,51 +0,0 @@
import os
import shutil
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json
from yunohost.tools import Migration
from yunohost.app import (_initialize_apps_catalog_system,
_update_apps_catalog,
APPS_CATALOG_CACHE,
APPS_CATALOG_CONF)
logger = getActionLogger('yunohost.migration')
LEGACY_APPS_CATALOG_CONF = '/etc/yunohost/appslists.json'
LEGACY_APPS_CATALOG_CONF_BACKUP = LEGACY_APPS_CATALOG_CONF + ".old"
class MyMigration(Migration):
"Migrate to the new future-proof apps catalog system"
def run(self):
if not os.path.exists(LEGACY_APPS_CATALOG_CONF):
logger.info("No need to do anything")
# Destroy old lecacy cache
if os.path.exists(APPS_CATALOG_CACHE):
shutil.rmtree(APPS_CATALOG_CACHE)
# and legacy cron
if os.path.exists("/etc/cron.daily/yunohost-fetch-appslists"):
os.remove("/etc/cron.daily/yunohost-fetch-appslists")
# Backup the legacy file
try:
legacy_catalogs = read_json(LEGACY_APPS_CATALOG_CONF)
# If there's only one catalog, we assume it's just the old official catalog
# Otherwise, warn the (power-?)users that they should migrate their old catalogs manually
if len(legacy_catalogs) > 1:
logger.warning("It looks like you had additional apps_catalog in the configuration file %s! YunoHost now uses %s instead, but it won't migrate your custom apps_catalog. You should do this manually. The old file has been backuped in %s." % (LEGACY_APPS_CATALOG_CONF, APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP))
except Exception as e:
logger.warning("Unable to parse the legacy conf %s (error : %s) ... migrating anyway" % (LEGACY_APPS_CATALOG_CONF, str(e)))
if os.path.exists(LEGACY_APPS_CATALOG_CONF):
os.rename(LEGACY_APPS_CATALOG_CONF, LEGACY_APPS_CATALOG_CONF_BACKUP)
_initialize_apps_catalog_system()
_update_apps_catalog()

View file

@ -1,31 +0,0 @@
import os
from moulinette.utils.log import getActionLogger
from moulinette.utils.filesystem import read_json
from yunohost.tools import Migration
from yunohost.app import app_setting, APPS_SETTING_PATH
logger = getActionLogger('yunohost.migration')
class MyMigration(Migration):
"""Remove legacy app status.json files"""
def run(self):
apps = os.listdir(APPS_SETTING_PATH)
for app in apps:
status_file = os.path.join(APPS_SETTING_PATH, app, "status.json")
if not os.path.exists(status_file):
continue
try:
status = read_json(status_file)
current_revision = status.get("remote", {}).get("revision", "?")
app_setting(app, 'current_revision', current_revision)
except Exception as e:
logger.warning("Could not migrate status.json from app %s: %s", (app, str(e)))
else:
os.system("rm %s" % status_file)

View file

@ -205,22 +205,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None,
key = keys[0]
# This mean that hmac-md5 is used
# (Re?)Trigger the migration to sha256 and return immediately.
# The actual update will be done in next run.
if "+157" in key:
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("migrate_to_tsig_sha256")
try:
migration.run(dyn_host, domain, key)
except Exception as e:
logger.error(m18n.n('migrations_migration_has_failed',
exception=e,
number=migration.number,
name=migration.name),
exc_info=1)
return
# Extract 'host', e.g. 'nohost.me' from 'foo.nohost.me'
host = domain.split('.')[1:]
host = '.'.join(host)

View file

@ -62,16 +62,6 @@ def regen_conf(operation_logger, names=[], with_diff=False, force=False, dry_run
"""
# Legacy code to automatically run the migration
# This is required because regen_conf is called before the migration call
# in debian's postinst script
if os.path.exists("/etc/yunohost/installed") \
and ("conffiles" in read_file("/etc/yunohost/services.yml") \
or not os.path.exists(REGEN_CONF_FILE)):
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("decouple_regenconf_from_services")
migration.run()
result = {}
# Return the list of pending conf

View file

@ -315,58 +315,3 @@ def test_apps_catalog_load_with_oudated_api_version(mocker):
for cache_file in glob.glob(APPS_CATALOG_CACHE + "/*"):
cache_json = read_json(cache_file)
assert cache_json["from_api_version"] == APPS_CATALOG_API_VERSION
def test_apps_catalog_migrate_legacy_explicitly():
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
mkdir(APPS_CATALOG_CACHE, 0o750, parents=True)
open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"foo":{}, "bar": {}}')
open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron")
from yunohost.tools import _get_migration_by_name
migration = _get_migration_by_name("futureproof_apps_catalog_system")
with requests_mock.Mocker() as m:
# Mock the server response with a dummy apps catalog
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
migration.run()
# Old conf shouldnt be there anymore (got renamed to .old)
assert not os.path.exists("/etc/yunohost/appslists.json")
# Old cache should have been removed
assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json")
# Cron should have been changed
assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read()
assert cron_job_is_there()
# Reading the apps_catalog should work
app_dict = _load_apps_catalog()["apps"]
assert "foo" in app_dict.keys()
assert "bar" in app_dict.keys()
def test_apps_catalog_migrate_legacy_implicitly():
open("/etc/yunohost/appslists.json", "w").write('{"yunohost": {"yolo":"swag"}}')
mkdir(APPS_CATALOG_CACHE, 0o750, parents=True)
open(APPS_CATALOG_CACHE+"/yunohost_old.json", "w").write('{"old_foo":{}, "old_bar": {}}')
open(APPS_CATALOG_CRON_PATH, "w").write("# Some old cron")
with requests_mock.Mocker() as m:
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
app_dict = _load_apps_catalog()["apps"]
assert "foo" in app_dict.keys()
assert "bar" in app_dict.keys()
# Old conf shouldnt be there anymore (got renamed to .old)
assert not os.path.exists("/etc/yunohost/appslists.json")
# Old cache should have been removed
assert not os.path.exists(APPS_CATALOG_CACHE+"/yunohost_old.json")
# Cron should have been changed
assert "/bin/bash" in open(APPS_CATALOG_CRON_PATH, "r").read()
assert cron_job_is_there()