[Windows] Security model for files permissions - STEP 1 (#6893)

This PR is the first part of #6497 to ease the integration, following the new plan propose by @bmw here: #6497 (comment)

This step 1 refactor existing certbot.compat module into certbot.compat.misc, without any logic changed. Package certbot.compat will host the new modules that constitute the security model for Windows.

* Create the certbot.compat package. Move logic in certbot.compat.misc

* Add doc

* Fix lint

* Correct mypy

* Update client.py
This commit is contained in:
Adrien Ferrand
2019-03-28 15:51:48 -07:00
committed by Brad Warren
parent b0fb570c1c
commit 6ce6c67932
26 changed files with 123 additions and 136 deletions
+6 -8
View File
@@ -12,24 +12,22 @@ import zope.interface
from acme import challenges from acme import challenges
from acme import crypto_util as acme_crypto_util from acme import crypto_util as acme_crypto_util
from acme.magic_typing import List, Dict, Set # pylint: disable=unused-import, no-name-in-module
from certbot import compat
from certbot import constants as core_constants from certbot import constants as core_constants
from certbot import crypto_util from certbot import crypto_util
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.plugins import common from certbot.plugins import common
from certbot_nginx import constants from certbot_nginx import constants
from certbot_nginx import display_ops from certbot_nginx import display_ops
from certbot_nginx import nginxparser
from certbot_nginx import parser
from certbot_nginx import http_01 from certbot_nginx import http_01
from certbot_nginx import obj # pylint: disable=unused-import from certbot_nginx import nginxparser
from acme.magic_typing import List, Dict, Set # pylint: disable=unused-import, no-name-in-module from certbot_nginx import obj # pylint: disable=unused-import
from certbot_nginx import parser
NAME_RANK = 0 NAME_RANK = 0
START_WILDCARD_RANK = 1 START_WILDCARD_RANK = 1
@@ -895,7 +893,7 @@ class NginxConfigurator(common.Installer):
have permissions of root. have permissions of root.
""" """
uid = compat.os_geteuid() uid = misc.os_geteuid()
util.make_or_verify_dir( util.make_or_verify_dir(
self.config.work_dir, core_constants.CONFIG_DIRS_MODE, uid) self.config.work_dir, core_constants.CONFIG_DIRS_MODE, uid)
util.make_or_verify_dir( util.make_or_verify_dir(
+4 -5
View File
@@ -7,22 +7,21 @@ import os
import shutil import shutil
import socket import socket
from cryptography.hazmat.primitives import serialization
import josepy as jose import josepy as jose
import pyrfc3339 import pyrfc3339
import pytz import pytz
import six import six
import zope.component import zope.component
from cryptography.hazmat.primitives import serialization
from acme import fields as acme_fields from acme import fields as acme_fields
from acme import messages from acme import messages
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -141,7 +140,7 @@ class AccountFileStorage(interfaces.AccountStorage):
""" """
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
util.make_or_verify_dir(config.accounts_dir, 0o700, compat.os_geteuid(), util.make_or_verify_dir(config.accounts_dir, 0o700, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
def _account_dir_path(self, account_id): def _account_dir_path(self, account_id):
@@ -324,7 +323,7 @@ class AccountFileStorage(interfaces.AccountStorage):
def _save(self, account, acme, regr_only): def _save(self, account, acme, regr_only):
account_dir_path = self._account_dir_path(account.id) account_dir_path = self._account_dir_path(account.id)
util.make_or_verify_dir(account_dir_path, 0o700, compat.os_geteuid(), util.make_or_verify_dir(account_dir_path, 0o700, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
try: try:
with open(self._regr_path(account_dir_path), "w") as regr_file: with open(self._regr_path(account_dir_path), "w") as regr_file:
+6 -5
View File
@@ -2,20 +2,21 @@
import datetime import datetime
import logging import logging
import os import os
import pytz
import re import re
import traceback import traceback
import pytz
import zope.component import zope.component
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
from certbot import compat
from certbot import crypto_util from certbot import crypto_util
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import ocsp from certbot import ocsp
from certbot import storage from certbot import storage
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.display import util as display_util from certbot.display import util as display_util
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -105,7 +106,7 @@ def lineage_for_certname(cli_config, certname):
"""Find a lineage object with name certname.""" """Find a lineage object with name certname."""
configs_dir = cli_config.renewal_configs_dir configs_dir = cli_config.renewal_configs_dir
# Verify the directory is there # Verify the directory is there
util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid()) util.make_or_verify_dir(configs_dir, mode=0o755, uid=misc.os_geteuid())
try: try:
renewal_file = storage.renewal_file_for_certname(cli_config, certname) renewal_file = storage.renewal_file_for_certname(cli_config, certname)
except errors.CertStorageError: except errors.CertStorageError:
@@ -375,7 +376,7 @@ def _search_lineages(cli_config, func, initial_rv, *args):
""" """
configs_dir = cli_config.renewal_configs_dir configs_dir = cli_config.renewal_configs_dir
# Verify the directory is there # Verify the directory is there
util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid()) util.make_or_verify_dir(configs_dir, mode=0o755, uid=misc.os_geteuid())
rv = initial_rv rv = initial_rv
for renewal_file in storage.renewal_conf_files(cli_config): for renewal_file in storage.renewal_conf_files(cli_config):
+6 -10
View File
@@ -4,14 +4,13 @@ import logging
import os import os
import platform import platform
import OpenSSL
import josepy as jose
import zope.component
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
# https://github.com/python/typeshed/blob/master/third_party/ # https://github.com/python/typeshed/blob/master/third_party/
# 2/cryptography/hazmat/primitives/asymmetric/rsa.pyi # 2/cryptography/hazmat/primitives/asymmetric/rsa.pyi
from cryptography.hazmat.primitives.asymmetric.rsa import generate_private_key # type: ignore from cryptography.hazmat.primitives.asymmetric.rsa import generate_private_key # type: ignore
import josepy as jose
import OpenSSL
import zope.component
from acme import client as acme_client from acme import client as acme_client
from acme import crypto_util as acme_crypto_util from acme import crypto_util as acme_crypto_util
@@ -20,11 +19,9 @@ from acme import messages
from acme.magic_typing import Optional # pylint: disable=unused-import,no-name-in-module from acme.magic_typing import Optional # pylint: disable=unused-import,no-name-in-module
import certbot import certbot
from certbot import account from certbot import account
from certbot import auth_handler from certbot import auth_handler
from certbot import cli from certbot import cli
from certbot import compat
from certbot import constants from certbot import constants
from certbot import crypto_util from certbot import crypto_util
from certbot import eff from certbot import eff
@@ -34,12 +31,11 @@ from certbot import interfaces
from certbot import reverter from certbot import reverter
from certbot import storage from certbot import storage
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.display import ops as display_ops
from certbot.display import enhancements from certbot.display import enhancements
from certbot.display import ops as display_ops
from certbot.plugins import selection as plugin_selection from certbot.plugins import selection as plugin_selection
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -466,7 +462,7 @@ class Client(object):
""" """
for path in cert_path, chain_path, fullchain_path: for path in cert_path, chain_path, fullchain_path:
util.make_or_verify_dir( util.make_or_verify_dir(
os.path.dirname(path), 0o755, compat.os_geteuid(), os.path.dirname(path), 0o755, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
+6
View File
@@ -0,0 +1,6 @@
"""
Compatibility layer to run certbot both on Linux and Windows.
This package contains all logic that needs to be implemented specifically for Linux and for Windows.
Then the rest of certbot code relies on this module to be platform agnostic.
"""
+2 -4
View File
@@ -1,8 +1,6 @@
""" """
Compatibility layer to run certbot both on Linux and Windows. This compat module handles various platform specific calls that do not fall into one
particular category.
This module contains all required platform specific code,
allowing the rest of Certbot codebase to be platform agnostic.
""" """
import os import os
import select import select
+3 -3
View File
@@ -2,14 +2,14 @@
import copy import copy
import os import os
from six.moves.urllib import parse # pylint: disable=import-error
import zope.interface import zope.interface
from six.moves.urllib import parse # pylint: disable=import-error
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
@zope.interface.implementer(interfaces.IConfig) @zope.interface.implementer(interfaces.IConfig)
@@ -70,7 +70,7 @@ class NamespaceConfig(object):
def accounts_dir_for_server_path(self, server_path): def accounts_dir_for_server_path(self, server_path):
"""Path to accounts directory based on server_path""" """Path to accounts directory based on server_path"""
server_path = compat.underscores_for_unsupported_characters_in_path(server_path) server_path = misc.underscores_for_unsupported_characters_in_path(server_path)
return os.path.join( return os.path.join(
self.namespace.config_dir, constants.ACCOUNTS_DIR, server_path) self.namespace.config_dir, constants.ACCOUNTS_DIR, server_path)
+7 -5
View File
@@ -1,10 +1,12 @@
"""Certbot constants.""" """Certbot constants."""
import logging import logging
import os import os
import pkg_resources import pkg_resources
from acme import challenges from acme import challenges
from certbot import compat
from certbot.compat import misc
SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins" SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
"""Setuptools entry point group name for plugins.""" """Setuptools entry point group name for plugins."""
@@ -14,7 +16,7 @@ OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
CLI_DEFAULTS = dict( CLI_DEFAULTS = dict(
config_files=[ config_files=[
os.path.join(compat.get_default_folder('config'), 'cli.ini'), os.path.join(misc.get_default_folder('config'), 'cli.ini'),
# http://freedesktop.org/wiki/Software/xdg-user-dirs/ # http://freedesktop.org/wiki/Software/xdg-user-dirs/
os.path.join(os.environ.get("XDG_CONFIG_HOME", "~/.config"), os.path.join(os.environ.get("XDG_CONFIG_HOME", "~/.config"),
"letsencrypt", "cli.ini"), "letsencrypt", "cli.ini"),
@@ -87,9 +89,9 @@ CLI_DEFAULTS = dict(
auth_cert_path="./cert.pem", auth_cert_path="./cert.pem",
auth_chain_path="./chain.pem", auth_chain_path="./chain.pem",
key_path=None, key_path=None,
config_dir=compat.get_default_folder('config'), config_dir=misc.get_default_folder('config'),
work_dir=compat.get_default_folder('work'), work_dir=misc.get_default_folder('work'),
logs_dir=compat.get_default_folder('logs'), logs_dir=misc.get_default_folder('logs'),
server="https://acme-v02.api.letsencrypt.org/directory", server="https://acme-v02.api.letsencrypt.org/directory",
# Plugins parsers # Plugins parsers
+9 -9
View File
@@ -12,24 +12,24 @@ import warnings
import pyrfc3339 import pyrfc3339
import six import six
import zope.component import zope.component
from OpenSSL import SSL # type: ignore
from OpenSSL import crypto
# https://github.com/python/typeshed/tree/master/third_party/2/cryptography
from cryptography import x509 # type: ignore
from cryptography.exceptions import InvalidSignature from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric.ec import ECDSA from cryptography.hazmat.primitives.asymmetric.ec import ECDSA
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicKey
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
# https://github.com/python/typeshed/tree/master/third_party/2/cryptography
from cryptography import x509 # type: ignore
from OpenSSL import crypto
from OpenSSL import SSL # type: ignore
from acme import crypto_util as acme_crypto_util from acme import crypto_util as acme_crypto_util
from acme.magic_typing import IO # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import IO # pylint: disable=unused-import, no-name-in-module
from certbot import compat
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -61,7 +61,7 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
config = zope.component.getUtility(interfaces.IConfig) config = zope.component.getUtility(interfaces.IConfig)
# Save file # Save file
util.make_or_verify_dir(key_dir, 0o700, compat.os_geteuid(), util.make_or_verify_dir(key_dir, 0o700, misc.os_geteuid(),
config.strict_permissions) config.strict_permissions)
key_f, key_path = util.unique_file( key_f, key_path = util.unique_file(
os.path.join(key_dir, keyname), 0o600, "wb") os.path.join(key_dir, keyname), 0o600, "wb")
@@ -92,8 +92,8 @@ def init_save_csr(privkey, names, path):
privkey.pem, names, must_staple=config.must_staple) privkey.pem, names, must_staple=config.must_staple)
# Save CSR # Save CSR
util.make_or_verify_dir(path, 0o755, compat.os_geteuid(), util.make_or_verify_dir(path, 0o755, misc.os_geteuid(),
config.strict_permissions) config.strict_permissions)
csr_f, csr_filename = util.unique_file( csr_f, csr_filename = util.unique_file(
os.path.join(path, "csr-certbot.pem"), 0o644, "wb") os.path.join(path, "csr-certbot.pem"), 0o644, "wb")
with csr_f: with csr_f:
+2 -3
View File
@@ -4,11 +4,10 @@ import os
import zope.component import zope.component
from certbot import compat
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.display import util as display_util from certbot.display import util as display_util
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -36,7 +35,7 @@ def get_email(invalid=False, optional=True):
"the client with --register-unsafely-without-email " "the client with --register-unsafely-without-email "
"but make sure you then backup your account key from " "but make sure you then backup your account key from "
"{0}\n\n".format(os.path.join( "{0}\n\n".format(os.path.join(
compat.get_default_folder('config'), 'accounts'))) misc.get_default_folder('config'), 'accounts')))
if optional: if optional:
if invalid: if invalid:
msg += unsafe_suggestion msg += unsafe_suggestion
+3 -3
View File
@@ -6,10 +6,10 @@ import textwrap
import zope.interface import zope.interface
from certbot import compat
from certbot import constants from certbot import constants
from certbot import interfaces
from certbot import errors from certbot import errors
from certbot import interfaces
from certbot.compat import misc
from certbot.display import completer from certbot.display import completer
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -79,7 +79,7 @@ def input_with_timeout(prompt=None, timeout=36000.0):
sys.stdout.write(prompt) sys.stdout.write(prompt)
sys.stdout.flush() sys.stdout.flush()
line = compat.readline_with_timeout(timeout, prompt) line = misc.readline_with_timeout(timeout, prompt)
if not line: if not line:
raise EOFError raise EOFError
+3 -2
View File
@@ -13,6 +13,7 @@ and properly flushed before program exit.
""" """
from __future__ import print_function from __future__ import print_function
import functools import functools
import logging import logging
import logging.handlers import logging.handlers
@@ -23,10 +24,10 @@ import traceback
from acme import messages from acme import messages
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import util from certbot import util
from certbot.compat import misc
# Logging format # Logging format
CLI_FMT = "%(message)s" CLI_FMT = "%(message)s"
@@ -134,7 +135,7 @@ def setup_log_file_handler(config, logfile, fmt):
# TODO: logs might contain sensitive data such as contents of the # TODO: logs might contain sensitive data such as contents of the
# private key! #525 # private key! #525
util.set_up_core_dir( util.set_up_core_dir(
config.logs_dir, 0o700, compat.os_geteuid(), config.strict_permissions) config.logs_dir, 0o700, misc.os_geteuid(), config.strict_permissions)
log_file_path = os.path.join(config.logs_dir, logfile) log_file_path = os.path.join(config.logs_dir, logfile)
try: try:
handler = logging.handlers.RotatingFileHandler( handler = logging.handlers.RotatingFileHandler(
+7 -8
View File
@@ -1,6 +1,7 @@
"""Certbot main entry point.""" """Certbot main entry point."""
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
from __future__ import print_function from __future__ import print_function
import functools import functools
import logging.handlers import logging.handlers
import os import os
@@ -14,12 +15,10 @@ from acme import errors as acme_errors
from acme.magic_typing import Union # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import Union # pylint: disable=unused-import, no-name-in-module
import certbot import certbot
from certbot import account from certbot import account
from certbot import cert_manager from certbot import cert_manager
from certbot import cli from certbot import cli
from certbot import client from certbot import client
from certbot import compat
from certbot import configuration from certbot import configuration
from certbot import constants from certbot import constants
from certbot import crypto_util from certbot import crypto_util
@@ -33,11 +32,11 @@ from certbot import reporter
from certbot import storage from certbot import storage
from certbot import updater from certbot import updater
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.display import util as display_util, ops as display_ops from certbot.display import util as display_util, ops as display_ops
from certbot.plugins import disco as plugins_disco from certbot.plugins import disco as plugins_disco
from certbot.plugins import selection as plug_sel
from certbot.plugins import enhancements from certbot.plugins import enhancements
from certbot.plugins import selection as plug_sel
USER_CANCELLED = ("User chose to cancel the operation and may " USER_CANCELLED = ("User chose to cancel the operation and may "
"reinvoke the client.") "reinvoke the client.")
@@ -1285,16 +1284,16 @@ def make_or_verify_needed_dirs(config):
""" """
util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE, util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE,
compat.os_geteuid(), config.strict_permissions) misc.os_geteuid(), config.strict_permissions)
util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE, util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE,
compat.os_geteuid(), config.strict_permissions) misc.os_geteuid(), config.strict_permissions)
hook_dirs = (config.renewal_pre_hooks_dir, hook_dirs = (config.renewal_pre_hooks_dir,
config.renewal_deploy_hooks_dir, config.renewal_deploy_hooks_dir,
config.renewal_post_hooks_dir,) config.renewal_post_hooks_dir,)
for hook_dir in hook_dirs: for hook_dir in hook_dirs:
util.make_or_verify_dir(hook_dir, util.make_or_verify_dir(hook_dir,
uid=compat.os_geteuid(), uid=misc.os_geteuid(),
strict=config.strict_permissions) strict=config.strict_permissions)
@@ -1345,7 +1344,7 @@ def main(cli_args=sys.argv[1:]):
# On windows, shell without administrative right cannot create symlinks required by certbot. # On windows, shell without administrative right cannot create symlinks required by certbot.
# So we check the rights before continuing. # So we check the rights before continuing.
compat.raise_for_non_administrative_windows_rights(config.verb) misc.raise_for_non_administrative_windows_rights(config.verb)
try: try:
log.post_arg_parse_setup(config) log.post_arg_parse_setup(config)
+3 -5
View File
@@ -17,14 +17,12 @@ import six
from acme import challenges from acme import challenges
from certbot import achallenges from certbot import achallenges
from certbot import compat
from certbot import errors from certbot import errors
from certbot.compat import misc
from certbot.display import util as display_util from certbot.display import util as display_util
from certbot.tests import acme_util from certbot.tests import acme_util
from certbot.tests import util as test_util from certbot.tests import util as test_util
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -171,14 +169,14 @@ class AuthenticatorTest(unittest.TestCase):
# Remove exec bit from permission check, so that it # Remove exec bit from permission check, so that it
# matches the file # matches the file
self.auth.perform([self.achall]) self.auth.perform([self.achall])
self.assertTrue(compat.compare_file_modes(os.stat(self.validation_path).st_mode, 0o644)) self.assertTrue(misc.compare_file_modes(os.stat(self.validation_path).st_mode, 0o644))
# Check permissions of the directories # Check permissions of the directories
for dirpath, dirnames, _ in os.walk(self.path): for dirpath, dirnames, _ in os.walk(self.path):
for directory in dirnames: for directory in dirnames:
full_path = os.path.join(dirpath, directory) full_path = os.path.join(dirpath, directory)
self.assertTrue(compat.compare_file_modes(os.stat(full_path).st_mode, 0o755)) self.assertTrue(misc.compare_file_modes(os.stat(full_path).st_mode, 0o755))
parent_gid = os.stat(self.path).st_gid parent_gid = os.stat(self.path).st_gid
parent_uid = os.stat(self.path).st_uid parent_uid = os.stat(self.path).st_uid
+5 -6
View File
@@ -10,12 +10,11 @@ import traceback
import six import six
import zope.component import zope.component
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot import util from certbot import util
from certbot.compat import misc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -66,7 +65,7 @@ class Reverter(object):
self.config = config self.config = config
util.make_or_verify_dir( util.make_or_verify_dir(
config.backup_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), config.backup_dir, constants.CONFIG_DIRS_MODE, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
def revert_temporary_config(self): def revert_temporary_config(self):
@@ -220,7 +219,7 @@ class Reverter(object):
""" """
util.make_or_verify_dir( util.make_or_verify_dir(
cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), cp_dir, constants.CONFIG_DIRS_MODE, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
op_fd, existing_filepaths = self._read_and_append( op_fd, existing_filepaths = self._read_and_append(
@@ -434,7 +433,7 @@ class Reverter(object):
cp_dir = self.config.in_progress_dir cp_dir = self.config.in_progress_dir
util.make_or_verify_dir( util.make_or_verify_dir(
cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(), cp_dir, constants.CONFIG_DIRS_MODE, misc.os_geteuid(),
self.config.strict_permissions) self.config.strict_permissions)
return cp_dir return cp_dir
@@ -576,7 +575,7 @@ class Reverter(object):
timestamp = self._checkpoint_timestamp() timestamp = self._checkpoint_timestamp()
final_dir = os.path.join(self.config.backup_dir, timestamp) final_dir = os.path.join(self.config.backup_dir, timestamp)
try: try:
compat.os_rename(self.config.in_progress_dir, final_dir) misc.os_rename(self.config.in_progress_dir, final_dir)
return return
except OSError: except OSError:
logger.warning("Extreme, unexpected race condition, retrying (%s)", timestamp) logger.warning("Extreme, unexpected race condition, retrying (%s)", timestamp)
+4 -5
View File
@@ -4,23 +4,22 @@ import glob
import logging import logging
import os import os
import re import re
import shutil
import stat import stat
import configobj import configobj
import parsedatetime import parsedatetime
import pytz import pytz
import shutil
import six import six
import certbot import certbot
from certbot import cli from certbot import cli
from certbot import compat
from certbot import constants from certbot import constants
from certbot import crypto_util from certbot import crypto_util
from certbot import errors
from certbot import error_handler from certbot import error_handler
from certbot import errors
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.plugins import common as plugins_common from certbot.plugins import common as plugins_common
from certbot.plugins import disco as plugins_disco from certbot.plugins import disco as plugins_disco
@@ -192,7 +191,7 @@ def update_configuration(lineagename, archive_dir, target, cli_config):
# Save only the config items that are relevant to renewal # Save only the config items that are relevant to renewal
values = relevant_values(vars(cli_config.namespace)) values = relevant_values(vars(cli_config.namespace))
write_renewal_config(config_filename, temp_filename, archive_dir, target, values) write_renewal_config(config_filename, temp_filename, archive_dir, target, values)
compat.os_rename(temp_filename, config_filename) misc.os_rename(temp_filename, config_filename)
return configobj.ConfigObj(config_filename) return configobj.ConfigObj(config_filename)
+3 -5
View File
@@ -12,11 +12,9 @@ import pytz
from acme import messages from acme import messages
from certbot import compat
from certbot import errors
import certbot.tests.util as test_util import certbot.tests.util as test_util
from certbot import errors
from certbot.compat import misc
KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
@@ -116,7 +114,7 @@ class AccountFileStorageTest(test_util.ConfigTestCase):
def test_init_creates_dir(self): def test_init_creates_dir(self):
self.assertTrue(os.path.isdir( self.assertTrue(os.path.isdir(
compat.underscores_for_unsupported_characters_in_path(self.config.accounts_dir))) misc.underscores_for_unsupported_characters_in_path(self.config.accounts_dir)))
@test_util.broken_on_windows @test_util.broken_on_windows
def test_save_and_restore(self): def test_save_and_restore(self):
+3 -2
View File
@@ -1,8 +1,9 @@
"""Tests for certbot.compat.""" """Tests for certbot.compat."""
import os import os
from certbot import compat
import certbot.tests.util as test_util import certbot.tests.util as test_util
from certbot.compat import misc
class OsReplaceTest(test_util.TempDirTestCase): class OsReplaceTest(test_util.TempDirTestCase):
"""Test to ensure consistent behavior of os_rename method""" """Test to ensure consistent behavior of os_rename method"""
@@ -15,7 +16,7 @@ class OsReplaceTest(test_util.TempDirTestCase):
open(dst, 'w').close() open(dst, 'w').close()
# On Windows, a direct call to os.rename will fail because dst already exists. # On Windows, a direct call to os.rename will fail because dst already exists.
compat.os_rename(src, dst) misc.os_rename(src, dst)
self.assertFalse(os.path.exists(src)) self.assertFalse(os.path.exists(src))
self.assertTrue(os.path.exists(dst)) self.assertTrue(os.path.exists(dst))
+3 -3
View File
@@ -4,12 +4,12 @@ import unittest
import mock import mock
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot.compat import misc
from certbot.tests import util as test_util from certbot.tests import util as test_util
class NamespaceConfigTest(test_util.ConfigTestCase): class NamespaceConfigTest(test_util.ConfigTestCase):
"""Tests for certbot.configuration.NamespaceConfig.""" """Tests for certbot.configuration.NamespaceConfig."""
@@ -48,7 +48,7 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
mock_constants.KEY_DIR = 'keys' mock_constants.KEY_DIR = 'keys'
mock_constants.TEMP_CHECKPOINT_DIR = 't' mock_constants.TEMP_CHECKPOINT_DIR = 't'
ref_path = compat.underscores_for_unsupported_characters_in_path( ref_path = misc.underscores_for_unsupported_characters_in_path(
'acc/acme-server.org:443/new') 'acc/acme-server.org:443/new')
self.assertEqual( self.assertEqual(
os.path.normpath(self.config.accounts_dir), os.path.normpath(self.config.accounts_dir),
+2 -3
View File
@@ -4,14 +4,13 @@ import socket
import tempfile import tempfile
import unittest import unittest
import six
import mock import mock
import six
from certbot import errors from certbot import errors
from certbot import interfaces from certbot import interfaces
from certbot.display import util as display_util from certbot.display import util as display_util
CHOICES = [("First", "Description1"), ("Second", "Description2")] CHOICES = [("First", "Description1"), ("Second", "Description2")]
TAGS = ["tag1", "tag2", "tag3"] TAGS = ["tag1", "tag2", "tag3"]
TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")] TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")]
@@ -32,7 +31,7 @@ class InputWithTimeoutTest(unittest.TestCase):
def test_input(self, prompt=None): def test_input(self, prompt=None):
expected = "foo bar" expected = "foo bar"
stdin = six.StringIO(expected + "\n") stdin = six.StringIO(expected + "\n")
with mock.patch("certbot.compat.select.select") as mock_select: with mock.patch("certbot.compat.misc.select.select") as mock_select:
mock_select.return_value = ([stdin], [], [],) mock_select.return_value = ([stdin], [], [],)
self.assertEqual(self._call(prompt), expected) self.assertEqual(self._call(prompt), expected)
+2 -3
View File
@@ -8,14 +8,13 @@ import unittest
import mock import mock
import six import six
from acme import messages from acme import messages
from acme.magic_typing import Optional # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import Optional # pylint: disable=unused-import, no-name-in-module
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.tests import util as test_util from certbot.tests import util as test_util
@@ -261,7 +260,7 @@ class TempHandlerTest(unittest.TestCase):
def test_permissions(self): def test_permissions(self):
self.assertTrue( self.assertTrue(
util.check_permissions(self.handler.path, 0o600, compat.os_geteuid())) util.check_permissions(self.handler.path, 0o600, misc.os_geteuid()))
def test_delete(self): def test_delete(self):
self.handler.close() self.handler.close()
+11 -12
View File
@@ -3,42 +3,41 @@
# pylint: disable=too-many-lines # pylint: disable=too-many-lines
from __future__ import print_function from __future__ import print_function
import datetime
import itertools import itertools
import json import json
import mock
import os import os
import shutil import shutil
import sys
import tempfile
import traceback import traceback
import unittest import unittest
import datetime
import pytz
import tempfile
import sys
import josepy as jose import josepy as jose
import mock
import pytz
import six import six
from six.moves import reload_module # pylint: disable=import-error from six.moves import reload_module # pylint: disable=import-error
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
import certbot.tests.util as test_util
from certbot import account from certbot import account
from certbot import cli from certbot import cli
from certbot import compat
from certbot import constants
from certbot import configuration from certbot import configuration
from certbot import constants
from certbot import crypto_util from certbot import crypto_util
from certbot import errors from certbot import errors
from certbot import interfaces # pylint: disable=unused-import from certbot import interfaces # pylint: disable=unused-import
from certbot import main from certbot import main
from certbot import updater from certbot import updater
from certbot import util from certbot import util
from certbot.compat import misc
from certbot.plugins import disco from certbot.plugins import disco
from certbot.plugins import enhancements from certbot.plugins import enhancements
from certbot.plugins import manual from certbot.plugins import manual
from certbot.plugins import null from certbot.plugins import null
import certbot.tests.util as test_util
CERT_PATH = test_util.vector_path('cert_512.pem') CERT_PATH = test_util.vector_path('cert_512.pem')
CERT = test_util.vector_path('cert_512.pem') CERT = test_util.vector_path('cert_512.pem')
CSR = test_util.vector_path('csr_512.der') CSR = test_util.vector_path('csr_512.der')
@@ -1587,7 +1586,7 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
for core_dir in (self.config.config_dir, self.config.work_dir,): for core_dir in (self.config.config_dir, self.config.work_dir,):
mock_util.set_up_core_dir.assert_any_call( mock_util.set_up_core_dir.assert_any_call(
core_dir, constants.CONFIG_DIRS_MODE, core_dir, constants.CONFIG_DIRS_MODE,
compat.os_geteuid(), self.config.strict_permissions misc.os_geteuid(), self.config.strict_permissions
) )
hook_dirs = (self.config.renewal_pre_hooks_dir, hook_dirs = (self.config.renewal_pre_hooks_dir,
@@ -1596,7 +1595,7 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
for hook_dir in hook_dirs: for hook_dir in hook_dirs:
# default mode of 755 is used # default mode of 755 is used
mock_util.make_or_verify_dir.assert_any_call( mock_util.make_or_verify_dir.assert_any_call(
hook_dir, uid=compat.os_geteuid(), hook_dir, uid=misc.os_geteuid(),
strict=self.config.strict_permissions) strict=self.config.strict_permissions)
+1 -2
View File
@@ -10,7 +10,6 @@ import mock
import six import six
from certbot import errors from certbot import errors
from certbot.tests import util as test_util from certbot.tests import util as test_util
@@ -356,7 +355,7 @@ class TestFullCheckpointsReverter(test_util.ConfigTestCase):
self.assertRaises( self.assertRaises(
errors.ReverterError, self.reverter.finalize_checkpoint, "Title") errors.ReverterError, self.reverter.finalize_checkpoint, "Title")
@mock.patch("certbot.reverter.compat.os_rename") @mock.patch("certbot.reverter.misc.os_rename")
def test_finalize_checkpoint_no_rename_directory(self, mock_rename): def test_finalize_checkpoint_no_rename_directory(self, mock_rename):
self.reverter.add_to_checkpoint(self.sets[0], "perm save") self.reverter.add_to_checkpoint(self.sets[0], "perm save")
+8 -10
View File
@@ -12,12 +12,10 @@ import pytz
import six import six
import certbot import certbot
from certbot import compat
from certbot import errors
from certbot.storage import ALL_FOUR
import certbot.tests.util as test_util import certbot.tests.util as test_util
from certbot import errors
from certbot.compat import misc
from certbot.storage import ALL_FOUR
CERT = test_util.load_cert('cert_512.pem') CERT = test_util.load_cert('cert_512.pem')
@@ -572,21 +570,21 @@ class RenewableCertTests(BaseRenewableCertTest):
for kind in ALL_FOUR: for kind in ALL_FOUR:
self._write_out_kind(kind, 1) self._write_out_kind(kind, 1)
self.test_rc.update_all_links_to(1) self.test_rc.update_all_links_to(1)
self.assertTrue(compat.compare_file_modes( self.assertTrue(misc.compare_file_modes(
os.stat(self.test_rc.version("privkey", 1)).st_mode, 0o600)) os.stat(self.test_rc.version("privkey", 1)).st_mode, 0o600))
os.chmod(self.test_rc.version("privkey", 1), 0o444) os.chmod(self.test_rc.version("privkey", 1), 0o444)
# If no new key, permissions should be the same (we didn't write any keys) # If no new key, permissions should be the same (we didn't write any keys)
self.test_rc.save_successor(1, b"newcert", None, b"new chain", self.config) self.test_rc.save_successor(1, b"newcert", None, b"new chain", self.config)
self.assertTrue(compat.compare_file_modes( self.assertTrue(misc.compare_file_modes(
os.stat(self.test_rc.version("privkey", 2)).st_mode, 0o444)) os.stat(self.test_rc.version("privkey", 2)).st_mode, 0o444))
# If new key, permissions should be kept as 644 # If new key, permissions should be kept as 644
self.test_rc.save_successor(2, b"newcert", b"new_privkey", b"new chain", self.config) self.test_rc.save_successor(2, b"newcert", b"new_privkey", b"new chain", self.config)
self.assertTrue(compat.compare_file_modes( self.assertTrue(misc.compare_file_modes(
os.stat(self.test_rc.version("privkey", 3)).st_mode, 0o644)) os.stat(self.test_rc.version("privkey", 3)).st_mode, 0o644))
# If permissions reverted, next renewal will also revert permissions of new key # If permissions reverted, next renewal will also revert permissions of new key
os.chmod(self.test_rc.version("privkey", 3), 0o400) os.chmod(self.test_rc.version("privkey", 3), 0o400)
self.test_rc.save_successor(3, b"newcert", b"new_privkey", b"new chain", self.config) self.test_rc.save_successor(3, b"newcert", b"new_privkey", b"new chain", self.config)
self.assertTrue(compat.compare_file_modes( self.assertTrue(misc.compare_file_modes(
os.stat(self.test_rc.version("privkey", 4)).st_mode, 0o600)) os.stat(self.test_rc.version("privkey", 4)).st_mode, 0o600))
@test_util.broken_on_windows @test_util.broken_on_windows
@@ -624,7 +622,7 @@ class RenewableCertTests(BaseRenewableCertTest):
self.config.live_dir, "README"))) self.config.live_dir, "README")))
self.assertTrue(os.path.exists(os.path.join( self.assertTrue(os.path.exists(os.path.join(
self.config.live_dir, "the-lineage.com", "README"))) self.config.live_dir, "the-lineage.com", "README")))
self.assertTrue(compat.compare_file_modes(os.stat(result.key_path).st_mode, 0o600)) self.assertTrue(misc.compare_file_modes(os.stat(result.key_path).st_mode, 0o600))
with open(result.fullchain, "rb") as f: with open(result.fullchain, "rb") as f:
self.assertEqual(f.read(), b"cert" + b"chain") self.assertEqual(f.read(), b"cert" + b"chain")
# Let's do it again and make sure it makes a different lineage # Let's do it again and make sure it makes a different lineage
+10 -10
View File
@@ -8,9 +8,9 @@ import mock
import six import six
from six.moves import reload_module # pylint: disable=import-error from six.moves import reload_module # pylint: disable=import-error
from certbot import compat
from certbot import errors
import certbot.tests.util as test_util import certbot.tests.util as test_util
from certbot import errors
from certbot.compat import misc
class RunScriptTest(unittest.TestCase): class RunScriptTest(unittest.TestCase):
@@ -119,7 +119,7 @@ class SetUpCoreDirTest(test_util.TempDirTestCase):
@mock.patch('certbot.util.lock_dir_until_exit') @mock.patch('certbot.util.lock_dir_until_exit')
def test_success(self, mock_lock): def test_success(self, mock_lock):
new_dir = os.path.join(self.tempdir, 'new') new_dir = os.path.join(self.tempdir, 'new')
self._call(new_dir, 0o700, compat.os_geteuid(), False) self._call(new_dir, 0o700, misc.os_geteuid(), False)
self.assertTrue(os.path.exists(new_dir)) self.assertTrue(os.path.exists(new_dir))
self.assertEqual(mock_lock.call_count, 1) self.assertEqual(mock_lock.call_count, 1)
@@ -127,7 +127,7 @@ class SetUpCoreDirTest(test_util.TempDirTestCase):
def test_failure(self, mock_make_or_verify): def test_failure(self, mock_make_or_verify):
mock_make_or_verify.side_effect = OSError mock_make_or_verify.side_effect = OSError
self.assertRaises(errors.Error, self._call, self.assertRaises(errors.Error, self._call,
self.tempdir, 0o700, compat.os_geteuid(), False) self.tempdir, 0o700, misc.os_geteuid(), False)
class MakeOrVerifyDirTest(test_util.TempDirTestCase): class MakeOrVerifyDirTest(test_util.TempDirTestCase):
@@ -144,7 +144,7 @@ class MakeOrVerifyDirTest(test_util.TempDirTestCase):
self.path = os.path.join(self.tempdir, "foo") self.path = os.path.join(self.tempdir, "foo")
os.mkdir(self.path, 0o600) os.mkdir(self.path, 0o600)
self.uid = compat.os_geteuid() self.uid = misc.os_geteuid()
def _call(self, directory, mode): def _call(self, directory, mode):
from certbot.util import make_or_verify_dir from certbot.util import make_or_verify_dir
@@ -154,11 +154,11 @@ class MakeOrVerifyDirTest(test_util.TempDirTestCase):
path = os.path.join(self.tempdir, "bar") path = os.path.join(self.tempdir, "bar")
self._call(path, 0o650) self._call(path, 0o650)
self.assertTrue(os.path.isdir(path)) self.assertTrue(os.path.isdir(path))
self.assertTrue(compat.compare_file_modes(os.stat(path).st_mode, 0o650)) self.assertTrue(misc.compare_file_modes(os.stat(path).st_mode, 0o650))
def test_existing_correct_mode_does_not_fail(self): def test_existing_correct_mode_does_not_fail(self):
self._call(self.path, 0o600) self._call(self.path, 0o600)
self.assertTrue(compat.compare_file_modes(os.stat(self.path).st_mode, 0o600)) self.assertTrue(misc.compare_file_modes(os.stat(self.path).st_mode, 0o600))
@test_util.skip_on_windows('Umask modes are mostly ignored on Windows.') @test_util.skip_on_windows('Umask modes are mostly ignored on Windows.')
def test_existing_wrong_mode_fails(self): def test_existing_wrong_mode_fails(self):
@@ -181,7 +181,7 @@ class CheckPermissionsTest(test_util.TempDirTestCase):
def setUp(self): def setUp(self):
super(CheckPermissionsTest, self).setUp() super(CheckPermissionsTest, self).setUp()
self.uid = compat.os_geteuid() self.uid = misc.os_geteuid()
def _call(self, mode): def _call(self, mode):
from certbot.util import check_permissions from certbot.util import check_permissions
@@ -223,8 +223,8 @@ class UniqueFileTest(test_util.TempDirTestCase):
def test_right_mode(self): def test_right_mode(self):
fd1, name1 = self._call(0o700) fd1, name1 = self._call(0o700)
fd2, name2 = self._call(0o600) fd2, name2 = self._call(0o600)
self.assertTrue(compat.compare_file_modes(0o700, os.stat(name1).st_mode)) self.assertTrue(misc.compare_file_modes(0o700, os.stat(name1).st_mode))
self.assertTrue(compat.compare_file_modes(0o600, os.stat(name2).st_mode)) self.assertTrue(misc.compare_file_modes(0o600, os.stat(name2).st_mode))
fd1.close() fd1.close()
fd2.close() fd2.close()
+4 -5
View File
@@ -10,20 +10,19 @@ import logging
import os import os
import platform import platform
import re import re
import six
import socket import socket
import subprocess import subprocess
from collections import OrderedDict from collections import OrderedDict
import configargparse import configargparse
import six
from acme.magic_typing import Tuple, Union # pylint: disable=unused-import, no-name-in-module from acme.magic_typing import Tuple, Union # pylint: disable=unused-import, no-name-in-module
from certbot import compat
from certbot import constants from certbot import constants
from certbot import errors from certbot import errors
from certbot import lock from certbot import lock
from certbot.compat import misc
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -204,7 +203,7 @@ def check_permissions(filepath, mode, uid=0):
""" """
file_stat = os.stat(filepath) file_stat = os.stat(filepath)
return compat.compare_file_modes(file_stat.st_mode, mode) and file_stat.st_uid == uid return misc.compare_file_modes(file_stat.st_mode, mode) and file_stat.st_uid == uid
def safe_open(path, mode="w", chmod=None, buffering=None): def safe_open(path, mode="w", chmod=None, buffering=None):