mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Support OpenDNSSEC 2.1: new ods-signer protocol
The communication between ods-signer and the socket-activated process has changed with OpenDNSSEC 2.1. Adapt ipa-ods-exporter to support also the new protocol. The internal database was also modified. Add a wrapper calling the right code (table names hab=ve changed, as well as table columns). With OpenDNSSEC the policy also needs to be explicitely loaded after ods-enforcer-db-setup has been run, with ods-enforcer policy import The command ods-ksmutil notify must be replace with ods-enforce flush. Related: https://pagure.io/freeipa/issue/8214 Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
parent
b857828180
commit
8080bf7b35
@ -22,7 +22,6 @@ import os
|
|||||||
import socket
|
import socket
|
||||||
import select
|
import select
|
||||||
import sys
|
import sys
|
||||||
import sqlite3
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import dateutil.tz
|
import dateutil.tz
|
||||||
@ -42,6 +41,8 @@ from ipaserver.dnssec.abshsm import sync_pkcs11_metadata, wrappingmech_name2id
|
|||||||
from ipaserver.dnssec.ldapkeydb import LdapKeyDB, str_hexlify
|
from ipaserver.dnssec.ldapkeydb import LdapKeyDB, str_hexlify
|
||||||
from ipaserver.dnssec.localhsm import LocalHSM
|
from ipaserver.dnssec.localhsm import LocalHSM
|
||||||
|
|
||||||
|
from ipaserver.dnssec import opendnssec
|
||||||
|
|
||||||
logger = logging.getLogger(os.path.basename(__file__))
|
logger = logging.getLogger(os.path.basename(__file__))
|
||||||
|
|
||||||
DAEMONNAME = 'ipa-ods-exporter'
|
DAEMONNAME = 'ipa-ods-exporter'
|
||||||
@ -233,26 +234,19 @@ def get_ldap_keys(ldap, zone_dn):
|
|||||||
|
|
||||||
def get_ods_keys(zone_name):
|
def get_ods_keys(zone_name):
|
||||||
# get zone ID
|
# get zone ID
|
||||||
cur = db.execute("SELECT id FROM zones WHERE LOWER(name)=LOWER(?)",
|
rows = db.get_zone_id(zone_name)
|
||||||
(zone_name,))
|
|
||||||
rows = cur.fetchall()
|
|
||||||
if len(rows) != 1:
|
if len(rows) != 1:
|
||||||
raise ValueError("exactly one DNS zone should exist in ODS DB")
|
raise ValueError("exactly one DNS zone should exist in ODS DB")
|
||||||
zone_id = rows[0][0]
|
zone_id = rows[0]
|
||||||
|
|
||||||
# get relevant keys for given zone ID:
|
# get relevant keys for given zone ID:
|
||||||
# ignore keys which were generated but not used yet
|
# ignore keys which were generated but not used yet
|
||||||
# key state check is using constants from
|
# key state check is using constants from
|
||||||
# OpenDNSSEC's enforcer/ksm/include/ksm/ksm.h
|
# OpenDNSSEC's enforcer/ksm/include/ksm/ksm.h
|
||||||
# WARNING! OpenDNSSEC version 1 and 2 are using different constants!
|
# WARNING! OpenDNSSEC version 1 and 2 are using different constants!
|
||||||
cur = db.execute("SELECT kp.HSMkey_id, kp.generate, kp.algorithm, "
|
rows = db.get_keys_for_zone(zone_id)
|
||||||
"dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, "
|
|
||||||
"dnsk.keytype, dnsk.state "
|
|
||||||
"FROM keypairs AS kp "
|
|
||||||
"JOIN dnsseckeys AS dnsk ON kp.id = dnsk.keypair_id "
|
|
||||||
"WHERE dnsk.zone_id = ?", (zone_id,))
|
|
||||||
keys = {}
|
keys = {}
|
||||||
for row in cur:
|
for row in rows:
|
||||||
key_data = sql2ldap_flags(row['keytype'])
|
key_data = sql2ldap_flags(row['keytype'])
|
||||||
if key_data.get('idnsSecKeyZONE') != 'TRUE':
|
if key_data.get('idnsSecKeyZONE') != 'TRUE':
|
||||||
raise ValueError("unexpected key type 0x%x" % row['keytype'])
|
raise ValueError("unexpected key type 0x%x" % row['keytype'])
|
||||||
@ -483,11 +477,13 @@ def receive_systemd_command():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
logger.debug('accepting new connection')
|
logger.debug('accepting new connection')
|
||||||
conn, _addr = sck.accept()
|
conn_tmp, _addr = sck.accept()
|
||||||
|
conn = opendnssec.ODSSignerConn(conn_tmp)
|
||||||
logger.debug('accepted new connection %s', repr(conn))
|
logger.debug('accepted new connection %s', repr(conn))
|
||||||
|
|
||||||
# this implements cmdhandler_handle_cmd() logic
|
# this implements cmdhandler_handle_cmd() logic
|
||||||
cmd = conn.recv(ODS_SE_MAXLINE).strip()
|
cmd = conn.read_cmd()
|
||||||
|
|
||||||
# ODS uses an ASCII protocol, the rest of the code expects str
|
# ODS uses an ASCII protocol, the rest of the code expects str
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
cmd = cmd.decode('ascii')
|
cmd = cmd.decode('ascii')
|
||||||
@ -548,9 +544,7 @@ def send_systemd_reply(conn, reply):
|
|||||||
# This is necessary to let Enforcer to unlock the ODS DB.
|
# This is necessary to let Enforcer to unlock the ODS DB.
|
||||||
if six.PY3:
|
if six.PY3:
|
||||||
reply = reply.encode('ascii')
|
reply = reply.encode('ascii')
|
||||||
conn.send(reply + b'\n')
|
conn.send_reply_and_close(reply)
|
||||||
conn.shutdown(socket.SHUT_RDWR)
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def cmd2ods_zone_name(cmd):
|
def cmd2ods_zone_name(cmd):
|
||||||
# ODS stores zone name without trailing period
|
# ODS stores zone name without trailing period
|
||||||
@ -566,7 +560,11 @@ def sync_zone(ldap, dns_dn, zone_name):
|
|||||||
Key material has to be synchronized elsewhere.
|
Key material has to be synchronized elsewhere.
|
||||||
Keep in mind that keys could be shared among multiple zones!"""
|
Keep in mind that keys could be shared among multiple zones!"""
|
||||||
logger.debug('%s: synchronizing zone "%s"', zone_name, zone_name)
|
logger.debug('%s: synchronizing zone "%s"', zone_name, zone_name)
|
||||||
ods_keys = get_ods_keys(zone_name)
|
try:
|
||||||
|
ods_keys = get_ods_keys(zone_name)
|
||||||
|
except ValueError as e:
|
||||||
|
logger.error(str(e))
|
||||||
|
return
|
||||||
ods_keys_id = set(ods_keys.keys())
|
ods_keys_id = set(ods_keys.keys())
|
||||||
|
|
||||||
ldap_zone = get_ldap_zone(ldap, dns_dn, zone_name)
|
ldap_zone = get_ldap_zone(ldap, dns_dn, zone_name)
|
||||||
@ -724,6 +722,14 @@ except KeyError as e:
|
|||||||
cmd = sys.argv[1]
|
cmd = sys.argv[1]
|
||||||
|
|
||||||
exitcode, msg, zone_name, cmd = parse_command(cmd)
|
exitcode, msg, zone_name, cmd = parse_command(cmd)
|
||||||
|
if exitcode:
|
||||||
|
logger.debug("parse_command returned exitcode: %d", exitcode)
|
||||||
|
if msg:
|
||||||
|
logger.debug("parse_command returned msg: %s", msg)
|
||||||
|
if zone_name:
|
||||||
|
logger.debug("parse_command returned zone_name: %s", zone_name)
|
||||||
|
if cmd:
|
||||||
|
logger.debug("parse_command returned cmd: %s", cmd)
|
||||||
|
|
||||||
if exitcode is not None:
|
if exitcode is not None:
|
||||||
if conn:
|
if conn:
|
||||||
@ -747,9 +753,7 @@ try:
|
|||||||
# Beware: Reply can be sent back only after DB is unlocked and closed
|
# Beware: Reply can be sent back only after DB is unlocked and closed
|
||||||
# otherwise ods-enforcerd will fail.
|
# otherwise ods-enforcerd will fail.
|
||||||
|
|
||||||
db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB)
|
db = opendnssec.ODSDBConnection()
|
||||||
db.row_factory = sqlite3.Row
|
|
||||||
db.execute('BEGIN')
|
|
||||||
|
|
||||||
if zone_name is not None:
|
if zone_name is not None:
|
||||||
# only one zone should be processed
|
# only one zone should be processed
|
||||||
@ -759,8 +763,8 @@ try:
|
|||||||
cleanup_ldap_zone(ldap, dns_dn, zone_name)
|
cleanup_ldap_zone(ldap, dns_dn, zone_name)
|
||||||
else:
|
else:
|
||||||
# process all zones
|
# process all zones
|
||||||
for zone_row in db.execute("SELECT name FROM zones"):
|
for zone_name in db.get_zones():
|
||||||
sync_zone(ldap, dns_dn, zone_row['name'])
|
sync_zone(ldap, dns_dn, zone_name)
|
||||||
|
|
||||||
### DNSSEC master: DNSSEC key material purging
|
### DNSSEC master: DNSSEC key material purging
|
||||||
# references to old key material were removed above in sync_zone()
|
# references to old key material were removed above in sync_zone()
|
||||||
|
@ -298,6 +298,36 @@ class BaseTaskNamespace:
|
|||||||
cmd = [paths.ODS_ENFORCER_DB_SETUP]
|
cmd = [paths.ODS_ENFORCER_DB_SETUP]
|
||||||
return ipautil.run(cmd, stdin="y", runas=constants.ODS_USER)
|
return ipautil.run(cmd, stdin="y", runas=constants.ODS_USER)
|
||||||
|
|
||||||
|
def run_ods_notify(self, **kwargs):
|
||||||
|
"""Notify ods-enforcerd to reload its conf."""
|
||||||
|
if paths.ODS_KSMUTIL is not None and os.path.exists(paths.ODS_KSMUTIL):
|
||||||
|
# OpenDNSSEC 1.4
|
||||||
|
cmd = [paths.ODS_KSMUTIL, 'notify']
|
||||||
|
else:
|
||||||
|
# OpenDNSSEC 2.x
|
||||||
|
cmd = [paths.ODS_ENFORCER, 'flush']
|
||||||
|
|
||||||
|
# run commands as ODS user
|
||||||
|
if os.geteuid() == 0:
|
||||||
|
kwargs['runas'] = constants.ODS_USER
|
||||||
|
|
||||||
|
return ipautil.run(cmd, **kwargs)
|
||||||
|
|
||||||
|
def run_ods_policy_import(self, **kwargs):
|
||||||
|
"""Run OpenDNSSEC manager command to import policy."""
|
||||||
|
# This step is needed with OpenDNSSEC 2.1 only
|
||||||
|
if paths.ODS_KSMUTIL is not None and os.path.exists(paths.ODS_KSMUTIL):
|
||||||
|
# OpenDNSSEC 1.4
|
||||||
|
return
|
||||||
|
|
||||||
|
# OpenDNSSEC 2.x
|
||||||
|
cmd = [paths.ODS_ENFORCER, 'policy', 'import']
|
||||||
|
|
||||||
|
# run commands as ODS user
|
||||||
|
if os.geteuid() == 0:
|
||||||
|
kwargs['runas'] = constants.ODS_USER
|
||||||
|
ipautil.run(cmd, **kwargs)
|
||||||
|
|
||||||
def run_ods_manager(self, params, **kwargs):
|
def run_ods_manager(self, params, **kwargs):
|
||||||
"""Run OpenDNSSEC manager command (ksmutil, enforcer)
|
"""Run OpenDNSSEC manager command (ksmutil, enforcer)
|
||||||
|
|
||||||
|
45
ipaserver/dnssec/_ods14.py
Normal file
45
ipaserver/dnssec/_ods14.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from ipaserver.dnssec._odsbase import AbstractODSDBConnection
|
||||||
|
from ipaserver.dnssec._odsbase import AbstractODSSignerConn
|
||||||
|
from ipaserver.dnssec._odsbase import ODS_SE_MAXLINE
|
||||||
|
|
||||||
|
|
||||||
|
class ODSDBConnection(AbstractODSDBConnection):
|
||||||
|
def get_zones(self):
|
||||||
|
cur = self._db.execute("SELECT name from zones")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
return [row['name'] for row in rows]
|
||||||
|
|
||||||
|
def get_zone_id(self, zone_name):
|
||||||
|
cur = self._db.execute(
|
||||||
|
"SELECT id FROM zones WHERE LOWER(name)=LOWER(?)",
|
||||||
|
(zone_name,))
|
||||||
|
rows = cur.fetchall()
|
||||||
|
return [row[0] for row in rows]
|
||||||
|
|
||||||
|
def get_keys_for_zone(self, zone_id):
|
||||||
|
cur = self._db.execute(
|
||||||
|
"SELECT kp.HSMkey_id, kp.generate, kp.algorithm, "
|
||||||
|
"dnsk.publish, dnsk.active, dnsk.retire, dnsk.dead, "
|
||||||
|
"dnsk.keytype, dnsk.state "
|
||||||
|
"FROM keypairs AS kp "
|
||||||
|
"JOIN dnsseckeys AS dnsk ON kp.id = dnsk.keypair_id "
|
||||||
|
"WHERE dnsk.zone_id = ?", (zone_id,))
|
||||||
|
for row in cur:
|
||||||
|
yield row
|
||||||
|
|
||||||
|
|
||||||
|
class ODSSignerConn(AbstractODSSignerConn):
|
||||||
|
def read_cmd(self):
|
||||||
|
cmd = self._conn.recv(ODS_SE_MAXLINE).strip()
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def send_reply_and_close(self, reply):
|
||||||
|
self._conn.send(reply + b'\n')
|
||||||
|
self._conn.shutdown(socket.SHUT_RDWR)
|
||||||
|
self._conn.close()
|
67
ipaserver/dnssec/_ods21.py
Normal file
67
ipaserver/dnssec/_ods21.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ipaserver.dnssec._odsbase import AbstractODSDBConnection
|
||||||
|
from ipaserver.dnssec._odsbase import AbstractODSSignerConn
|
||||||
|
from ipaserver.dnssec._odsbase import ODS_SE_MAXLINE
|
||||||
|
|
||||||
|
CLIENT_OPC_STDOUT = 0
|
||||||
|
CLIENT_OPC_EXIT = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ODSDBConnection(AbstractODSDBConnection):
|
||||||
|
def get_zones(self):
|
||||||
|
cur = self._db.execute("SELECT name from zone")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
return [row['name'] for row in rows]
|
||||||
|
|
||||||
|
def get_zone_id(self, zone_name):
|
||||||
|
cur = self._db.execute(
|
||||||
|
"SELECT id FROM zone WHERE LOWER(name)=LOWER(?)",
|
||||||
|
(zone_name,))
|
||||||
|
rows = cur.fetchall()
|
||||||
|
return [row[0] for row in rows]
|
||||||
|
|
||||||
|
def get_keys_for_zone(self, zone_id):
|
||||||
|
cur = self._db.execute(
|
||||||
|
"SELECT hsmk.locator, hsmk.inception, hsmk.algorithm, "
|
||||||
|
"hsmk.keyType, hsmk.state "
|
||||||
|
"FROM hsmKey AS hsmk "
|
||||||
|
"JOIN keyData AS kd ON hsmk.id = kd.hsmKeyId "
|
||||||
|
"WHERE kd.zoneId = ?", (zone_id,))
|
||||||
|
for row in cur:
|
||||||
|
key = dict()
|
||||||
|
key['HSMkey_id'] = row['locator']
|
||||||
|
key['generate'] = str(datetime.fromtimestamp(row['inception']))
|
||||||
|
key['algorithm'] = row['algorithm']
|
||||||
|
key['publish'] = key['generate']
|
||||||
|
key['active'] = None
|
||||||
|
key['retire'] = None
|
||||||
|
key['dead'] = None
|
||||||
|
if row['keyType'] == 2:
|
||||||
|
key['keytype'] = 256
|
||||||
|
elif row['keyType'] == 1:
|
||||||
|
key['keytype'] = 257
|
||||||
|
key['state'] = row['state']
|
||||||
|
yield key
|
||||||
|
|
||||||
|
|
||||||
|
class ODSSignerConn(AbstractODSSignerConn):
|
||||||
|
def read_cmd(self):
|
||||||
|
msg = self._conn.recv(ODS_SE_MAXLINE)
|
||||||
|
_opc = int(msg[0])
|
||||||
|
msglen = int(msg[1]) << 8 + int(msg[2])
|
||||||
|
cmd = msg[3:msglen - 1].strip()
|
||||||
|
return cmd
|
||||||
|
|
||||||
|
def send_reply_and_close(self, reply):
|
||||||
|
prefix = bytearray([CLIENT_OPC_STDOUT, len(reply) >> 8,
|
||||||
|
len(reply) & 255])
|
||||||
|
self._conn.sendall(prefix + reply)
|
||||||
|
# 2nd message: CLIENT_OPC_EXIT, then len, msg len, exit code
|
||||||
|
prefix = bytearray([CLIENT_OPC_EXIT, 0, 1, 0])
|
||||||
|
self._conn.sendall(prefix)
|
||||||
|
self._conn.close()
|
52
ipaserver/dnssec/_odsbase.py
Normal file
52
ipaserver/dnssec/_odsbase.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
import six
|
||||||
|
import abc
|
||||||
|
import sqlite3
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
|
ODS_SE_MAXLINE = 1024 # from ODS common/config.h
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class AbstractODSDBConnection():
|
||||||
|
"""Abstract class representing the Connection to ODS database."""
|
||||||
|
def __init__(self):
|
||||||
|
"""Creates a connection to the kasp database."""
|
||||||
|
self._db = sqlite3.connect(paths.OPENDNSSEC_KASP_DB)
|
||||||
|
self._db.row_factory = sqlite3.Row
|
||||||
|
self._db.execute('BEGIN')
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_zones(self):
|
||||||
|
"""Returns a list of zone names."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_zone_id(self, zone_name):
|
||||||
|
"""Returns a list of zone ids for the given zone_name."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get_keys_for_zone(self, zone_id):
|
||||||
|
"""Returns a list of keys for the given zone_id."""
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Closes the connection to the kasp database."""
|
||||||
|
self._db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class AbstractODSSignerConn():
|
||||||
|
"""Abstract class representing the Connection to ods-signer."""
|
||||||
|
def __init__(self, conn):
|
||||||
|
"""Initializes the object with a socket conn."""
|
||||||
|
self._conn = conn
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def read_cmd(self):
|
||||||
|
"""Reads the next command on the connection."""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def send_reply_and_close(self, reply):
|
||||||
|
"""Sends the reply on the connection."""
|
@ -159,27 +159,48 @@ class ODSMgr:
|
|||||||
|
|
||||||
def add_ods_zone(self, uuid, name):
|
def add_ods_zone(self, uuid, name):
|
||||||
zone_path = '%s%s' % (ENTRYUUID_PREFIX, uuid)
|
zone_path = '%s%s' % (ENTRYUUID_PREFIX, uuid)
|
||||||
|
if name != dns.name.root:
|
||||||
|
name = name.relativize(dns.name.root)
|
||||||
cmd = ['zone', 'add', '--zone', str(name), '--input', zone_path]
|
cmd = ['zone', 'add', '--zone', str(name), '--input', zone_path]
|
||||||
output = self.ksmutil(cmd)
|
output = None
|
||||||
logger.info('%s', output)
|
try:
|
||||||
self.notify_enforcer()
|
output = self.ksmutil(cmd)
|
||||||
|
except ipautil.CalledProcessError as e:
|
||||||
|
# Zone already exists in HSM
|
||||||
|
if e.returncode == 1 \
|
||||||
|
and str(e.output).endswith(str(name) + ' already exists!'):
|
||||||
|
# Just return
|
||||||
|
return
|
||||||
|
if output is not None:
|
||||||
|
logger.info('%s', output)
|
||||||
|
self.notify_enforcer()
|
||||||
|
|
||||||
def del_ods_zone(self, name):
|
def del_ods_zone(self, name):
|
||||||
# ods-ksmutil blows up if zone name has period at the end
|
# ods-ksmutil blows up if zone name has period at the end
|
||||||
name = name.relativize(dns.name.root)
|
if name != dns.name.root:
|
||||||
|
name = name.relativize(dns.name.root)
|
||||||
# detect if name is root zone
|
# detect if name is root zone
|
||||||
if name == dns.name.empty:
|
if name == dns.name.empty:
|
||||||
name = dns.name.root
|
name = dns.name.root
|
||||||
cmd = ['zone', 'delete', '--zone', str(name)]
|
cmd = ['zone', 'delete', '--zone', str(name)]
|
||||||
output = self.ksmutil(cmd)
|
output = None
|
||||||
logger.info('%s', output)
|
try:
|
||||||
self.notify_enforcer()
|
output = self.ksmutil(cmd)
|
||||||
self.cleanup_signer(name)
|
except ipautil.CalledProcessError as e:
|
||||||
|
# Zone already doesn't exist in HSM
|
||||||
|
if e.returncode == 1 \
|
||||||
|
and str(e.output).endswith(str(name) + ' not found!'):
|
||||||
|
# Just cleanup signer, no need to notify enforcer
|
||||||
|
self.cleanup_signer(name)
|
||||||
|
return
|
||||||
|
if output is not None:
|
||||||
|
logger.info('%s', output)
|
||||||
|
self.notify_enforcer()
|
||||||
|
self.cleanup_signer(name)
|
||||||
|
|
||||||
def notify_enforcer(self):
|
def notify_enforcer(self):
|
||||||
cmd = ['notify']
|
result = tasks.run_ods_notify(capture_output=True)
|
||||||
output = self.ksmutil(cmd)
|
logger.info('%s', result.output)
|
||||||
logger.info('%s', output)
|
|
||||||
|
|
||||||
def cleanup_signer(self, zone_name):
|
def cleanup_signer(self, zone_name):
|
||||||
cmd = ['ods-signer', 'ldap-cleanup', str(zone_name)]
|
cmd = ['ods-signer', 'ldap-cleanup', str(zone_name)]
|
||||||
|
12
ipaserver/dnssec/opendnssec.py
Normal file
12
ipaserver/dnssec/opendnssec.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
|
# pylint: disable=unused-import
|
||||||
|
if paths.ODS_KSMUTIL is not None and os.path.exists(paths.ODS_KSMUTIL):
|
||||||
|
from ._ods14 import ODSDBConnection, ODSSignerConn
|
||||||
|
else:
|
||||||
|
from ._ods21 import ODSDBConnection, ODSSignerConn
|
@ -314,6 +314,7 @@ class OpenDNSSECInstance(service.Service):
|
|||||||
|
|
||||||
def __start(self):
|
def __start(self):
|
||||||
self.restart() # needed to reload conf files
|
self.restart() # needed to reload conf files
|
||||||
|
tasks.run_ods_policy_import()
|
||||||
|
|
||||||
def uninstall(self):
|
def uninstall(self):
|
||||||
if not self.is_configured():
|
if not self.is_configured():
|
||||||
|
Loading…
Reference in New Issue
Block a user