Skip to content

Commit 97dfb35

Browse files
Dan Lavudanlavu
authored andcommitted
adding dns server and zone utilities to the samba role
1 parent 1d76935 commit 97dfb35

File tree

1 file changed

+237
-3
lines changed

1 file changed

+237
-3
lines changed

sssd_test_framework/roles/samba.py

Lines changed: 237 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from pytest_mh.conn import ProcessResult
1212

1313
from ..hosts.samba import SambaHost
14-
from ..misc import attrs_parse, to_list_of_strings
14+
from ..misc import attrs_parse, ip_version, to_list_of_strings
1515
from ..utils.ldap import LDAPRecordAttributes
1616
from .base import BaseLinuxLDAPRole, BaseObject, DeleteAttribute
1717
from .generic import GenericPasswordPolicy
@@ -28,6 +28,8 @@
2828
"SambaAutomount",
2929
"SambaSudoRule",
3030
"SambaGPO",
31+
"SambaDNSServer",
32+
"SambaDNSZone",
3133
]
3234

3335

@@ -301,6 +303,38 @@ def test_example(client: Client, samba: Samba):
301303
"""
302304
return SambaComputer(self, name)
303305

306+
def dns(self) -> SambaDNSServer:
307+
"""
308+
Get DNS server object.
309+
310+
Get methods use dig and is parsed by jc. The data from jc contains several nested dict,
311+
but two are returned as a tuple, ``answer, authority``.
312+
313+
.. code-block:: python
314+
:caption: Example usage
315+
316+
# Create forward zone and add forward record
317+
zone = samba.dns().zone("example.test").create()
318+
zone.add_record("client", "172.16.200.15")
319+
320+
# Create reverse zone and add reverse record
321+
zone = samba.dns().zone("10.0.10.in-addr.arpa").create()
322+
zone.add_ptr_record("client.example.test", 15)
323+
324+
# Add forward record to default domain
325+
samba.dns().zone(samba.domain).add_record("client", "1.2.3.4")
326+
327+
# Add a global forwarder
328+
samba.dns().add_forwarder("1.1.1.1")
329+
330+
# Remove a global forwarder
331+
samba.dns().remove_forwarder("1.1.1.1")
332+
333+
# Clear all forwarders
334+
samba.dns().clear_forwarders()
335+
"""
336+
return SambaDNSServer(self)
337+
304338
def gpo(self, name: str) -> SambaGPO:
305339
"""
306340
Get group policy object.
@@ -887,8 +921,8 @@ class SambaComputer(SambaObject):
887921

888922
def __init__(self, role: Samba, name: str) -> None:
889923
"""
890-
:param role: AD role object.
891-
:type role: AD
924+
:param role: Samba role object.
925+
:type role: Samba
892926
:param name: Computer name.
893927
:type name: str
894928
"""
@@ -1172,6 +1206,206 @@ def lockout(self, duration: int, attempts: int) -> SambaPasswordPolicy:
11721206
return self
11731207

11741208

1209+
class SambaDNSServer(BaseObject[SambaHost, Samba]):
1210+
"""
1211+
DNS management utilities.
1212+
"""
1213+
1214+
def __init__(self, role: Samba):
1215+
"""
1216+
:param role: Samba host object.
1217+
:type role: SambaHost
1218+
"""
1219+
super().__init__(role)
1220+
1221+
self.domain: str = role.domain
1222+
"""Domain name."""
1223+
1224+
self.server: str = role.server
1225+
"""Server name."""
1226+
1227+
self.naming_context: str = role.naming_context
1228+
"""Naming context."""
1229+
1230+
self.credentials: str = f" --username={self.role.host.adminuser} --password={self.role.host.adminpw}"
1231+
"""Credentials to manage GPOs."""
1232+
1233+
self.smb_conf: str = "/etc/samba/smb.conf"
1234+
1235+
def zone(self, name: str) -> SambaDNSZone:
1236+
"""
1237+
Get SambaDNSZone object.
1238+
1239+
:param name: Zone name.
1240+
:type name: str
1241+
:return: SambaDNSZone object.
1242+
:rtype: SambaDNSZone
1243+
"""
1244+
return SambaDNSZone(self.role, name)
1245+
1246+
def get_forwarders(self) -> list[str] | None:
1247+
"""
1248+
Get DNS global forwarders.
1249+
1250+
Global forwarders are configured in /etc/smb.conf
1251+
1252+
:return: DNS global forwarders.
1253+
:rtype: list[str]
1254+
"""
1255+
result = [line.strip() for line in self.host.fs.read(self.smb_conf).split("\n")]
1256+
if result is not None and isinstance(result, list):
1257+
for i in result:
1258+
if "dns forwarder" in i:
1259+
# The additional split is to support more than one server
1260+
return i.split("=")[1].strip().split(" ")
1261+
return None
1262+
1263+
def add_forwarder(self, ip_address: str) -> SambaDNSServer:
1264+
"""
1265+
Add a DNS server forwarder.
1266+
1267+
:param ip_address: IP address.
1268+
:type ip_address: str
1269+
:return: Self.
1270+
:rtype: SambaDNSServer
1271+
"""
1272+
self.host.fs.backup(self.smb_conf)
1273+
self.host.fs.sed(f"s/dns forwarder = .*/ & {ip_address}/", self.smb_conf, ["-i"])
1274+
self.host.svc.reload("samba.service")
1275+
return self
1276+
1277+
def remove_forwarder(self, ip_address: str) -> None:
1278+
"""
1279+
Remove a DNS server forwarder.
1280+
1281+
:param ip_address: IP address.
1282+
:type ip_address: str
1283+
"""
1284+
ip_address = ip_address.replace(".", "\\.").strip()
1285+
self.host.fs.backup(self.smb_conf)
1286+
self.host.fs.sed(f"/dns forwarder/s/ {ip_address}//", self.smb_conf, ["-i"])
1287+
self.host.svc.reload("samba.service")
1288+
1289+
def clear_forwarders(self) -> None:
1290+
"""
1291+
Clear all DNS server forwarders.
1292+
1293+
Samba has one global forwarder enabled by default.
1294+
"""
1295+
forwarders = self.get_forwarders()
1296+
1297+
if isinstance(forwarders, list) and not None:
1298+
for forwarder in forwarders:
1299+
self.remove_forwarder(forwarder)
1300+
1301+
def list_zones(self) -> list[str]:
1302+
"""
1303+
List zones.
1304+
1305+
:return: List of zones.
1306+
:rtype: list[str]
1307+
"""
1308+
result = self.host.conn.run(f"samba-tool dns zonelist {self.server} {self.credentials}").stdout_lines
1309+
result = [i for i in result if "pszZoneName" in i]
1310+
result = [z.split(":")[1].strip() for z in result]
1311+
1312+
return result
1313+
1314+
1315+
class SambaDNSZone(SambaDNSServer):
1316+
"""
1317+
DNS zone management.
1318+
"""
1319+
1320+
def __init__(self, role: Samba, name: str):
1321+
"""
1322+
:param role: Samba host object.
1323+
:type role: SambaHost
1324+
:param name: DNS zone name.
1325+
:type name: str
1326+
"""
1327+
super().__init__(role)
1328+
1329+
self.zone_name: str = name
1330+
"""Zone name."""
1331+
1332+
def create(self) -> SambaDNSZone:
1333+
"""
1334+
Create new zone.
1335+
1336+
:return: SambaDNSServer object.
1337+
:rtype: SambaDNSServer
1338+
"""
1339+
self.host.conn.run(f"samba-tool dns zonecreate {self.server} {self.zone_name} {self.credentials}")
1340+
return self
1341+
1342+
def delete(self) -> None:
1343+
"""
1344+
Delete zone.
1345+
"""
1346+
self.host.conn.run(f"samba-tool dns zonedelete {self.server} {self.zone_name} {self.credentials}")
1347+
1348+
def add_record(self, name: str, data: str) -> SambaDNSZone:
1349+
"""
1350+
Add DNS record.
1351+
1352+
If ``data`` is a str, a forward record will be added.
1353+
If an integer a reverse record will be added.
1354+
1355+
:param name: Record name.
1356+
:type name: str | int
1357+
:param data: Record data.
1358+
:type data: str
1359+
:return: SambaDNSZone object.
1360+
:rtype: SambaDNSZone
1361+
"""
1362+
args = ""
1363+
1364+
if isinstance(data, int):
1365+
args = f" {name} PTR {str(data)} {self.credentials}"
1366+
elif isinstance(data, str) and ip_version(data) == 4:
1367+
args = f" {name} A {data} {self.credentials}"
1368+
elif isinstance(data, str) and ip_version(data) == 6:
1369+
args = f" {name} AAAA {data} {self.credentials}"
1370+
1371+
self.host.conn.run(f"samba-tool dns add {self.server} {self.zone_name} {args}")
1372+
return self
1373+
1374+
def delete_record(self, name: str) -> None:
1375+
"""
1376+
Delete DNS record.
1377+
1378+
:param name: Name of the record.
1379+
:type name: str
1380+
"""
1381+
if "in-addr" in self.zone_name:
1382+
record_type = "PTR"
1383+
data = self.host.conn.run(f"dig -x +short {name}").stdout.strip()
1384+
else:
1385+
data = self.host.conn.run(f"dig +short {name}").stdout.strip()
1386+
record_type = "AAAA" if ":" in data else "A"
1387+
1388+
self.role.host.conn.run(
1389+
f"samba-tool dns delete "
1390+
f"{self.server} {self.zone_name} "
1391+
f"{name} {record_type} {data} "
1392+
f"{self.credentials}"
1393+
)
1394+
1395+
def print(self) -> str:
1396+
"""
1397+
Prints all dns records in a zone as text.
1398+
1399+
:return: Print zone data.
1400+
:rtype: str
1401+
"""
1402+
result = self.host.conn.run(
1403+
f"samba-tool dns query {self.server} {self.zone_name} @ ALL {self.credentials}"
1404+
).stdout
1405+
1406+
return result
1407+
1408+
11751409
SambaOrganizationalUnit: TypeAlias = LDAPOrganizationalUnit[SambaHost, Samba]
11761410
SambaAutomount: TypeAlias = LDAPAutomount[SambaHost, Samba]
11771411
SambaSudoRule: TypeAlias = LDAPSudoRule[SambaHost, Samba, SambaUser, SambaGroup]

0 commit comments

Comments
 (0)