Import Upstream version 4.12.4

This commit is contained in:
geos_one
2025-08-12 22:28:56 +02:00
parent 03a8170b15
commit 9181ee2487
1629 changed files with 874094 additions and 554378 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -21,20 +21,17 @@ from __future__ import absolute_import
import logging
import os
import tempfile
import shutil
from urllib.parse import urlsplit
from ipalib.install import certmonger, certstore
from ipalib.facts import is_ipa_configured
from ipalib.install.kinit import kinit_keytab
from ipapython import admintool, certdb, ipaldap, ipautil
from ipaplatform import services
from ipaplatform.paths import paths
from ipaplatform.tasks import tasks
from ipalib import api, errors, x509
from ipalib.constants import IPA_CA_NICKNAME, RENEWAL_CA_NAME
from ipalib.constants import FQDN, IPA_CA_NICKNAME, RENEWAL_CA_NAME
from ipalib.util import check_client_configuration
logger = logging.getLogger(__name__)
@@ -54,12 +51,28 @@ class CertUpdate(admintool.AdminTool):
def run(self):
check_client_configuration()
api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA)
api.finalize()
old_krb5ccname = os.environ.get('KRB5CCNAME')
os.environ['KRB5_CLIENT_KTNAME'] = '/etc/krb5.keytab'
os.environ['KRB5CCNAME'] = "MEMORY:"
api.Backend.rpcclient.connect()
run_with_args(api)
api.Backend.rpcclient.disconnect()
try:
api.bootstrap(context='cli_installer', confdir=paths.ETC_IPA)
api.finalize()
api.Backend.rpcclient.connect()
run_with_args(api)
api.Backend.rpcclient.disconnect()
except errors.CCacheError:
logger.error(
"Unable to obtain credentials for %s from /etc/krb5.keytab",
FQDN
)
raise
finally:
if old_krb5ccname is None:
del os.environ['KRB5CCNAME']
else:
os.environ['KRB5CCNAME'] = old_krb5ccname
def run_with_args(api):
@@ -73,44 +86,39 @@ def run_with_args(api):
server = urlsplit(api.env.jsonrpc_uri).hostname
ldap = ipaldap.LDAPClient.from_hostname_secure(server)
tmpdir = tempfile.mkdtemp(prefix="tmp-")
ccache_name = os.path.join(tmpdir, 'ccache')
old_krb5ccname = os.environ.get('KRB5CCNAME')
try:
principal = str('host/%s@%s' % (api.env.host, api.env.realm))
kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_name)
os.environ['KRB5CCNAME'] = ccache_name
result = api.Command.ca_is_enabled(version=u'2.107')
ca_enabled = result['result']
except (errors.CommandError, errors.NetworkError):
result = api.Command.env(server=True, version=u'2.0')
ca_enabled = result['result']['enable_ra']
try:
result = api.Command.ca_is_enabled(version=u'2.107')
ca_enabled = result['result']
except (errors.CommandError, errors.NetworkError):
result = api.Command.env(server=True, version=u'2.0')
ca_enabled = result['result']['enable_ra']
ldap.gssapi_bind()
ldap.gssapi_bind()
certs = certstore.get_ca_certs(
ldap, api.env.basedn, api.env.realm, ca_enabled)
certs = certstore.get_ca_certs(
ldap, api.env.basedn, api.env.realm, ca_enabled)
if ca_enabled:
lwcas = api.Command.ca_find()['result']
else:
lwcas = []
if ca_enabled:
lwcas = api.Command.ca_find()['result']
else:
lwcas = []
finally:
if old_krb5ccname is None:
del os.environ['KRB5CCNAME']
else:
os.environ['KRB5CCNAME'] = old_krb5ccname
shutil.rmtree(tmpdir)
# update client certs before KDC and HTTPd are restarted.
update_client(certs)
if is_ipa_configured():
# look up CA servers before service restarts
resp = api.Command.server_role_find(
role_servrole=u'CA server',
status='enabled',
)
ca_servers = [server['server_server'] for server in resp['result']]
update_server(certs)
# pylint: disable=import-error,ipa-forbidden-import
from ipaserver.install import cainstance
# pylint: enable=import-error,ipa-forbidden-import
# pylint: disable=ipa-forbidden-import
from ipaserver.install import cainstance, custodiainstance
# pylint: enable=ipa-forbidden-import
# Add LWCA tracking requests. Only execute if *this server*
# has CA installed (ca_enabled indicates CA-ful topology).
@@ -121,7 +129,23 @@ def run_with_args(api):
logger.exception(
"Failed to add lightweight CA tracking requests")
update_client(certs)
try:
update_server_ra_config(
cainstance, custodiainstance,
api.env.enable_ra, api.env.ca_host, ca_servers,
)
except Exception:
logger.exception("Failed to update RA config")
# update_server_ra_config possibly updated default.conf;
# restart httpd to pick up changes.
if services.knownservices.httpd.is_running():
services.knownservices.httpd.restart()
# update_client() may have updated KDC cert bundle; restart KDC to pick
# up changes.
if services.knownservices.krb5kdc.is_running():
services.knownservices.krb5kdc.restart()
def update_client(certs):
@@ -154,9 +178,6 @@ def update_server(certs):
if services.knownservices.dirsrv.is_running():
services.knownservices.dirsrv.restart(instance)
if services.knownservices.httpd.is_running():
services.knownservices.httpd.restart()
criteria = {
'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
'cert-nickname': IPA_CA_NICKNAME,
@@ -179,7 +200,7 @@ def update_server(certs):
#
logger.debug("resubmitting certmonger request '%s'", request_id)
certmonger.resubmit_request(
request_id, ca='dogtag-ipa-ca-renew-agent-reuse', profile='')
request_id, ca='dogtag-ipa-ca-renew-agent-reuse')
try:
state = certmonger.wait_for_request(request_id, timeout)
except RuntimeError:
@@ -199,6 +220,43 @@ def update_server(certs):
update_file(paths.CACERT_PEM, certs)
def update_server_ra_config(
cainstance, custodiainstance,
enable_ra, ca_host, ca_servers,
):
"""
After promoting a CA-less deployment to CA-ful, or after removal
of a CA server from the topology, it may be necessary to update
the default.conf ca_host setting on non-CA replicas.
"""
if len(ca_servers) == 0:
return # nothing to do
# In case ca_host setting is not valid, select a new ca_host.
# Just choose the first server. (Choosing a server in the same
# location might be better, but we should only incur that
# complexity if a need is proven).
new_ca_host = ca_servers[0]
if not enable_ra:
# RA is not enabled, but deployment is CA-ful.
# Retrieve IPA RA credential and update ipa.conf.
cainstance.CAInstance.configure_certmonger_renewal_helpers()
custodia = custodiainstance.CustodiaInstance(
host_name=api.env.host,
realm=api.env.realm,
custodia_peer=new_ca_host,
)
cainstance.import_ra_key(custodia)
cainstance.update_ipa_conf(new_ca_host)
elif ca_host not in ca_servers:
# RA is enabled but ca_host is not among the deployment's
# CA servers. Set a valid ca_host.
cainstance.update_ipa_conf(new_ca_host)
def update_file(filename, certs, mode=0o644):
certs = (c[0] for c in certs if c[2] is not False)
try:

View File

@@ -29,37 +29,29 @@ import shutil
import time
import tempfile
import gssapi
import warnings
try:
from xml.etree import cElementTree as etree
except ImportError:
from xml.etree import ElementTree as etree
import SSSDConfig
# pylint: disable=import-error
from six.moves.urllib.parse import urlsplit
# pylint: enable=import-error
from optparse import OptionParser # pylint: disable=deprecated-module
from ipapython import ipachangeconf
from ipaclient.install import ipadiscovery
from ipaclient import discovery
from ipaclient.install.client import (
CLIENT_NOT_CONFIGURED,
CLIENT_ALREADY_CONFIGURED,
)
from ipalib import api, errors
from ipalib.install import sysrestore
from ipalib.install.kinit import kinit_keytab
from ipalib.kinit import kinit_keytab
from ipalib.util import check_client_configuration
from ipapython import ipautil
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
from ipaplatform.constants import constants
from ipaplatform.tasks import tasks
from ipaplatform import services
from ipaplatform.paths import paths
from ipapython.admintool import ScriptError
from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
@@ -67,7 +59,7 @@ logger = logging.getLogger(os.path.basename(__file__))
def parse_options():
usage = "%prog [options]\n"
parser = OptionParser(usage=usage)
parser = IPAOptionParser(usage=usage)
parser.add_option("--server", dest="server", help="FQDN of IPA server")
parser.add_option(
"--location",
@@ -75,14 +67,6 @@ def parse_options():
default="default",
help="Automount location",
)
parser.add_option(
"-S",
"--no-sssd",
dest="sssd",
action="store_false",
default=True,
help="Do not configure the client to use SSSD for automount",
)
parser.add_option(
"--idmap-domain",
dest="idmapdomain",
@@ -90,6 +74,7 @@ def parse_options():
help="nfs domain for idmapd.conf",
)
parser.add_option(
"-d",
"--debug",
dest="debug",
action="store_true",
@@ -148,52 +133,6 @@ def wait_for_sssd():
)
def configure_xml(fstore):
authconf = paths.AUTOFS_LDAP_AUTH_CONF
fstore.backup_file(authconf)
try:
tree = etree.parse(authconf)
except IOError as e:
logger.debug('Unable to open file %s', e)
logger.debug('Creating new from template')
tree = etree.ElementTree(
element=etree.Element('autofs_ldap_sasl_conf')
)
element = tree.getroot()
if element.tag != 'autofs_ldap_sasl_conf':
raise RuntimeError('Invalid XML root in file %s' % authconf)
element.set('usetls', 'no')
element.set('tlsrequired', 'no')
element.set('authrequired', 'yes')
element.set('authtype', 'GSSAPI')
element.set('clientprinc', 'host/%s@%s' % (api.env.host, api.env.realm))
try:
tree.write(authconf, xml_declaration=True, encoding='UTF-8')
except IOError as e:
print("Unable to write %s: %s" % (authconf, e))
else:
print("Configured %s" % authconf)
def configure_nsswitch(statestore, options):
"""
This function was deprecated. Use ipaplatform.tasks.
Point automount to ldap in nsswitch.conf.
This function is for non-SSSD setups only.
"""
warnings.warn(
"Use ipaplatform.tasks.tasks.enable_ldap_automount",
DeprecationWarning,
stacklevel=2
)
return tasks.enable_ldap_automount(statestore)
def configure_autofs_sssd(fstore, statestore, autodiscover, options):
try:
sssdconfig = SSSDConfig.SSSDConfig()
@@ -249,43 +188,6 @@ def configure_autofs_sssd(fstore, statestore, autodiscover, options):
wait_for_sssd()
def configure_autofs(fstore, statestore, autodiscover, server, options):
"""
fstore: the FileStore to back up files in
options.server: the IPA server to use
options.location: the Automount location to use
"""
if not autodiscover:
ldap_uri = "ldap://%s" % server
else:
ldap_uri = "ldap:///%s" % api.env.basedn
search_base = str(
DN(
('cn', options.location),
api.env.container_automount,
api.env.basedn,
)
)
replacevars = {
'MAP_OBJECT_CLASS': 'automountMap',
'ENTRY_OBJECT_CLASS': 'automount',
'MAP_ATTRIBUTE': 'automountMapName',
'ENTRY_ATTRIBUTE': 'automountKey',
'VALUE_ATTRIBUTE': 'automountInformation',
'SEARCH_BASE': search_base,
'LDAP_URI': ldap_uri,
}
ipautil.backup_config_and_replace_variables(
fstore, paths.SYSCONFIG_AUTOFS, replacevars=replacevars
)
tasks.restore_context(paths.SYSCONFIG_AUTOFS)
statestore.backup_state('autofs', 'sssd', False)
print("Configured %s" % paths.SYSCONFIG_AUTOFS)
def configure_autofs_common(fstore, statestore, options):
autofs = services.knownservices.autofs
statestore.backup_state('autofs', 'enabled', autofs.is_enabled())
@@ -312,7 +214,6 @@ def configure_autofs_common(fstore, statestore, options):
def uninstall(fstore, statestore):
RESTORE_FILES = [
paths.SYSCONFIG_AUTOFS,
paths.AUTOFS_LDAP_AUTH_CONF,
paths.SYSCONFIG_NFS,
paths.IDMAPD_CONF,
]
@@ -439,14 +340,16 @@ def configure_nfs(fstore, statestore, options):
def configure_automount():
try:
check_client_configuration()
except ScriptError as e:
print(e.msg)
sys.exit(e.rval)
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
if not statestore.get_state('installation', 'automount'):
# not called from ipa-client-install
try:
check_client_configuration()
except ScriptError as e:
print(e.msg)
sys.exit(e.rval)
fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
options, _args = parse_options()
@@ -481,12 +384,12 @@ def configure_automount():
sys.exit(CLIENT_ALREADY_CONFIGURED)
autodiscover = False
ds = ipadiscovery.IPADiscovery()
ds = discovery.IPADiscovery()
if not options.server:
print("Searching for IPA server...")
ret = ds.search(ca_cert_path=ca_cert_path)
logger.debug('Executing DNS discovery')
if ret == ipadiscovery.NO_LDAP_SERVER:
if ret == discovery.NO_LDAP_SERVER:
logger.debug('Autodiscovery did not find LDAP server')
s = urlsplit(api.env.xmlrpc_uri)
server = [s.netloc]
@@ -506,14 +409,14 @@ def configure_automount():
server = options.server
logger.debug("Verifying that %s is an IPA server", server)
ldapret = ds.ipacheckldap(server, api.env.realm, ca_cert_path)
if ldapret[0] == ipadiscovery.NO_ACCESS_TO_LDAP:
if ldapret[0] == discovery.NO_ACCESS_TO_LDAP:
print("Anonymous access to the LDAP server is disabled.")
print("Proceeding without strict verification.")
print(
"Note: This is not an error if anonymous access has been "
"explicitly restricted."
)
elif ldapret[0] == ipadiscovery.NO_TLS_LDAP:
elif ldapret[0] == discovery.NO_TLS_LDAP:
logger.warning("Unencrypted access to LDAP is not supported.")
elif ldapret[0] != 0:
sys.exit('Unable to confirm that %s is an IPA server' % server)
@@ -573,16 +476,8 @@ def configure_automount():
sys.exit("Installation aborted")
try:
if not options.sssd:
tasks.enable_ldap_automount(statestore)
configure_nfs(fstore, statestore, options)
if options.sssd:
configure_autofs_sssd(fstore, statestore, autodiscover, options)
else:
configure_xml(fstore)
configure_autofs(
fstore, statestore, autodiscover, server, options
)
configure_autofs_sssd(fstore, statestore, autodiscover, options)
configure_autofs_common(fstore, statestore, options)
except Exception as e:
logger.debug('Raised exception %s', e)

View File

@@ -9,7 +9,6 @@ import logging
import os
import gssapi
from urllib.parse import urlsplit
from optparse import OptionParser # pylint: disable=deprecated-module
from contextlib import contextmanager
from ipaclient import discovery
@@ -31,6 +30,7 @@ from ipaplatform.constants import constants
from ipaplatform import services
from ipapython.admintool import ScriptError
from samba import generate_random_password
from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(logging.DEBUG)
@@ -39,8 +39,8 @@ logger.setLevel(logging.DEBUG)
@contextmanager
def use_api_as_principal(principal, keytab):
with ipautil.private_ccache() as ccache_file:
old_principal = getattr(context, "principal", None)
try:
old_principal = getattr(context, "principal", None)
name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
store = {"ccache": ccache_file, "client_keytab": keytab}
gssapi.Credentials(name=name, usage="initiate", store=store)
@@ -68,7 +68,7 @@ def use_api_as_principal(principal, keytab):
def parse_options():
usage = "%prog [options]\n"
parser = OptionParser(usage=usage)
parser = IPAOptionParser(usage=usage)
parser.add_option(
"--server",
dest="server",
@@ -102,6 +102,7 @@ def parse_options():
help="force installation by redoing all steps",
)
parser.add_option(
"-d",
"--debug",
dest="debug",
action="store_true",
@@ -172,8 +173,8 @@ def retrieve_domain_information(api):
return []
l_domain = dict()
for key in trust_keymap:
l_domain[key] = result.get(trust_keymap[key], [None])[0]
for key, val in trust_keymap.items():
l_domain[key] = result.get(val, [None])[0]
# Pull down ID range and other details of our domain
#
@@ -445,13 +446,17 @@ def uninstall(fstore, statestore, options):
fstore.restore_file(paths.SMB_CONF)
# Remove samba's persistent and temporary tdb files
tdb_files = [
tdb_file
for tdb_file in os.listdir(paths.SAMBA_DIR)
if tdb_file.endswith(".tdb")
]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# in /var/lib/samba and /var/lib/samba/private
for smbpath in (paths.SAMBA_DIR,
os.path.join(paths.SAMBA_DIR, "private"),
os.path.join(paths.SAMBA_DIR, "lock")):
tdb_files = [
os.path.join(smbpath, tdb_file)
for tdb_file in os.listdir(smbpath)
if tdb_file.endswith(".tdb")
]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# Remove our keys from samba's keytab
if os.path.exists(paths.SAMBA_KEYTAB):

View File

@@ -29,10 +29,12 @@ import os
import pwd
import logging
import smtplib
import ssl
import time
from collections import deque
from datetime import datetime, timedelta
from datetime import datetime, timedelta, timezone
UTC = timezone.utc
from email.utils import formataddr, formatdate
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
@@ -55,11 +57,15 @@ EPN_CONFIG = {
"smtp_port": 25,
"smtp_user": None,
"smtp_password": None,
"smtp_client_cert": None,
"smtp_client_key": None,
"smtp_client_key_pass": None,
"smtp_timeout": 60,
"smtp_security": "none",
"smtp_admin": "root@localhost",
"smtp_delay": None,
"mail_from": None,
"mail_from_name": "IPA-EPN",
"notify_ttls": "28,14,7,3,1",
"msg_charset": "utf8",
"msg_subtype": "plain",
@@ -81,7 +87,7 @@ def drop_privileges(new_username="daemon", new_groupname="daemon"):
os.setuid(grp.getgrnam(new_groupname).gr_gid)
if os.getuid() == 0:
raise Exception()
raise errors.RequiresRoot("Cannot drop privileges!")
logger.debug(
"Dropped privileges to user=%s, group=%s",
@@ -205,6 +211,7 @@ class EPN(admintool.AdminTool):
def __init__(self, options, args):
super(EPN, self).__init__(options, args)
self._conn = None
self._ssl_context = None
self._expiring_password_user_list = EPNUserList()
self._ldap_data = []
self._date_ranges = []
@@ -291,12 +298,15 @@ class EPN(admintool.AdminTool):
logger.error("IPA client is not configured on this system.")
raise admintool.ScriptError()
# tasks required privileges
self._get_krb5_ticket()
self._read_configuration()
self._validate_configuration()
self._parse_configuration()
self._get_connection()
self._read_ipa_configuration()
self._create_ssl_context()
drop_privileges()
if self.options.mailtest:
self._gentestdata()
@@ -316,6 +326,7 @@ class EPN(admintool.AdminTool):
smtp_timeout=api.env.smtp_timeout,
smtp_username=api.env.smtp_user,
smtp_password=api.env.smtp_password,
ssl_context=self._ssl_context,
x_mailer=self.command_name,
msg_subtype=api.env.msg_subtype,
msg_charset=api.env.msg_charset,
@@ -327,7 +338,7 @@ class EPN(admintool.AdminTool):
of days in the future.
If only nbdays_end is specified, the range is 1d long.
"""
now = datetime.utcnow()
now = datetime.now(tz=UTC)
today_at_midnight = datetime.combine(now, datetime.min.time())
range_end = today_at_midnight + timedelta(days=nbdays_end)
if nbdays_start is not None:
@@ -388,7 +399,7 @@ class EPN(admintool.AdminTool):
"""
if api.env.smtp_security.lower() not in ("none", "starttls", "ssl"):
raise RuntimeError(
"smtp_security must be one of: " "none, starttls or ssl"
"smtp_security must be one of: none, starttls or ssl"
)
if api.env.smtp_user is not None and api.env.smtp_password is None:
raise RuntimeError("smtp_user set and smtp_password is not")
@@ -457,6 +468,20 @@ class EPN(admintool.AdminTool):
return self._conn
def _create_ssl_context(self):
"""Create SSL context.
This must be done before the dropping priviliges to allow
read in the smtp client's certificate and private key if specified.
"""
if api.env.smtp_security.lower() in ("starttls", "ssl"):
self._ssl_context = ssl.create_default_context()
if api.env.smtp_client_cert:
self._ssl_context.load_cert_chain(
certfile=api.env.smtp_client_cert,
keyfile=api.env.smtp_client_key,
password=str(api.env.smtp_client_key_pass),
)
def _fetch_data_from_ldap(self, date_range):
"""Run a LDAP query to fetch a list of user entries whose passwords
would expire in the near future. Store in self._ldap_data.
@@ -542,11 +567,12 @@ class EPN(admintool.AdminTool):
mail_body=body,
subscribers=ast.literal_eval(entry["mail"]),
mail_from=mail_from,
mail_from_name=api.env.mail_from_name,
)
now = datetime.utcnow()
now = datetime.now(tz=UTC)
expdate = datetime.strptime(
entry["krbpasswordexpiration"],
'%Y-%m-%d %H:%M:%S')
'%Y-%m-%d %H:%M:%S').replace(tzinfo=UTC)
logger.debug(
"Notified %s (%s). Password expiring in %d days at %s.",
entry["mail"], entry["uid"], (expdate - now).days,
@@ -558,7 +584,7 @@ class EPN(admintool.AdminTool):
def _gentestdata(self):
"""Generate a sample user to process through the template.
"""
expdate = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
expdate = datetime.now(tz=UTC).strftime('%Y-%m-%d %H:%M:%S')
entry = dict(
uid=["SAUSER"],
cn=["SAMPLE USER"],
@@ -603,15 +629,15 @@ class MTAClient:
smtp_timeout=60,
smtp_username=None,
smtp_password=None,
ssl_context=None,
):
# We only support "none" (cleartext) for now.
# Future values: "ssl", "starttls"
self._security_protocol = security_protocol
self._smtp_hostname = smtp_hostname
self._smtp_port = smtp_port
self._smtp_timeout = smtp_timeout
self._username = smtp_username
self._password = smtp_password
self._ssl_context = ssl_context
# This should not be touched
self._conn = None
@@ -664,6 +690,7 @@ class MTAClient:
host=self._smtp_hostname,
port=self._smtp_port,
timeout=self._smtp_timeout,
context=self._ssl_context,
)
except (socketerror, smtplib.SMTPException) as e:
msg = \
@@ -685,20 +712,14 @@ class MTAClient:
e,
)
if (
self._conn.has_extn("STARTTLS")
and self._security_protocol.lower() == "starttls"
):
if self._security_protocol.lower() == "starttls":
try:
self._conn.starttls()
self._conn.starttls(context=self._ssl_context)
self._conn.ehlo()
except smtplib.SMTPException as e:
logger.error(
raise RuntimeError(
"IPA-EPN: Unable to create an encrypted session to "
"%s:%s: %s",
self._smtp_hostname,
self._smtp_port,
e,
"%s:%s: %s" % (self._smtp_hostname, self._smtp_port, e)
)
if self._username and self._password:
@@ -749,6 +770,7 @@ class MailUserAgent:
smtp_timeout=60,
smtp_username=None,
smtp_password=None,
ssl_context=None,
x_mailer=None,
msg_subtype="plain",
msg_charset="utf8",
@@ -772,6 +794,7 @@ class MailUserAgent:
smtp_timeout=smtp_timeout,
smtp_username=smtp_username,
smtp_password=smtp_password,
ssl_context=ssl_context,
)
def cleanup(self):
@@ -779,12 +802,13 @@ class MailUserAgent:
def send_message(
self, mail_subject=None, mail_body=None, subscribers=None,
mail_from=None
mail_from=None, mail_from_name=None
):
"""Given mail_subject, mail_body, and subscribers, composes
the message and sends it.
"""
if None in [mail_subject, mail_body, subscribers, mail_from]:
if None in [mail_subject, mail_body, subscribers,
mail_from, mail_from_name]:
logger.error("IPA-EPN: Tried to send an empty message.")
return False
self._compose_message(
@@ -792,6 +816,7 @@ class MailUserAgent:
mail_body=mail_body,
subscribers=subscribers,
mail_from=mail_from,
mail_from_name=mail_from_name,
)
self._mta_client.send_message(
message_str=self._message_str, subscribers=subscribers
@@ -799,7 +824,8 @@ class MailUserAgent:
return True
def _compose_message(
self, mail_subject, mail_body, subscribers, mail_from
self, mail_subject, mail_body, subscribers,
mail_from, mail_from_name
):
"""The composer creates a MIME multipart message.
"""
@@ -809,7 +835,7 @@ class MailUserAgent:
self._subscribers = subscribers
self._msg = MIMEMultipart(_charset=self._charset)
self._msg["From"] = formataddr(("IPA-EPN", mail_from))
self._msg["From"] = formataddr((mail_from_name, mail_from))
self._msg["To"] = ", ".join(self._subscribers)
self._msg["Date"] = formatdate(localtime=True)
self._msg["Subject"] = Header(self._subject, self._charset)

View File

@@ -82,13 +82,16 @@ def sync_chrony():
# Restart chronyd
services.knownservices.chronyd.restart()
sync_attempt_count = 3
# chrony attempt count to sync with configiured servers
# each next attempt is tried after 10seconds of timeot
# 3 attempts means: if first immidiate attempt fails
# there is 10s delay between next attempts
args = [paths.CHRONYC, 'waitsync', str(sync_attempt_count), '-d']
# chrony attempt count to sync with configured servers. Each attempt is
# retried after $interval seconds.
# 4 attempts with 3s interval result in a maximum delay of 9 seconds.
sync_attempt_count = 4
sync_attempt_interval = 3
args = [
paths.CHRONYC, '-d', 'waitsync',
# max-tries, max-correction, max-skew, interval
str(sync_attempt_count), '0', '0', str(sync_attempt_interval)
]
try:
logger.info('Attempting to sync time with chronyc.')