📜 ⬆️ ⬇️

Samba4 - using Python Scripting Interface

Samba4 has a built-in Python interface. Many utilities (samba-tool, for example) are fully implemented in Python using this interface.

Everything done from the LDAP interface can be done on Samba 4 Python Scripting. The advantages are file access, which means high speed, some features that are not in LDAP. For example, you can take a hash of user passwords from one database and transfer it to another. Yes, and the users themselves with their SID-s, passwords and all the rest to transfer to another domain (without problems with SID-history).

Documentation is not enough, but there are examples in the <samba-source> / python / samba directory, if there are sources, otherwise somewhere in /usr/lib/python2.7/dist-packages/samba .

Of greatest interest is the samdb.py file - the implementation of most operations in AD.
')
Suppose we have installed Samba4 in the AD domain controller configuration. Let's try to connect to the AD database from a Python program. To get started, import the necessary libraries:

#!/usr/bin/env python # -*- coding: utf-8 -*- import ldb from samba.samdb import SamDB from samba.auth import system_session from samba.ndr import ndr_pack, ndr_unpack from samba.dcerpc import security import samba.param import base64 import binascii 

Connect to the main base /sam.ldb:

 lp = samba.param.LoadParm() lp.load(samba.param.default_path()) # lp.load("/etc/samba/smb.conf") sam = SamDB(lp=lp,session_info=system_session()) 

(It is also possible to connect with a non-standard location of the files and directories of the Samba4 installation and even to a stand-alone time base. About this below.)

Now the sam object allows you to search and modify the database AD in full accordance with the syntax of LDAP.
For example, database search (base is an LDAP tree node of the type CN = Users, DC = myDom, DC = lan, expression is an optional selection condition, attrs is a list of desired attributes):

 res = sam.search(base=base, expression=expression, attrs=[*]) 

Let users lie in the OS:

 base = "OU=myUsers,DC=myDom,DC=lan" #        base = "CN=Users,DC=myDom,DC=lan" 

Create a user “tst” with the password “secret”. The SamDB class has a ready-made method, newuser (), but you can try it like this:

 newUsr = "tst" usrPass = "secret" ld = {'dn': 'CN=%s,%s' % (newUsr,base), "sAMAccountName": newUsr, "userPrincipalName": "%s@%s" % (newUsr,"myDom.lan"), "objectClass": "user", "displayName": newUsr, "description": newUsr, "homeDirectory": r"\\%s\users\%s" % ("myHost",newUsr), 'scriptPath': "loginScr.cmd", } sam.transaction_start() try: sam.add(ld) sam.setpassword("(samAccountName=%s)" % ldb.binary_encode(newUsr), usrPass, False) except: sam.transaction_cancel() print '!!!error' else: sam.transaction_commit() 

As you can see, SamDB supports transactions.

The entire AD database, if it is not very large, can be viewed (and edited) with the command:

 :~# ldbedit -e nano -H /var/lib/samba/private/sam.ldb 

But it is better to limit the selection with the option -s or -b (base), for example, -b 'CN = RID Manager $, CN = System, DC = myDom, DC = com' .

Transferring password hashes can be done according to this scheme:

Suppose we have an old AD database - also on Samba4. You can get a replica of the database from Win AD by connecting a new Samba4 installation as an additional AD DC — a well-documented and simple procedure — see here .

Copy and connect to it - call the connection sam0. Connection with non-standard paths (let it be copied to / tmp / priv and its smb.conf there):

 lp0 = samba.param.LoadParm() lp0.load('/tmp/priv/smb.conf') lp0.set('private directory','/tmp/priv') sam0 = SamDB(lp=lp0,session_info=system_session()) 

To get the entire list of users, and with passwords, we will make the following request:

 res = sam0.search(base="DC=oldDom,DC=myDom,DC=ru",expression="(&(objectCategory=person)(objectClass=user))", attrs=['*','unicodePwd']) 

We will go through the user base and add them to the new database. Schematically it looks like this:

 for r in res: dn = str(r.dn)#   DN ,     .  ! ------- sd = ndr_unpack(security.dom_sid,r['objectSid'][0])#  SID ,     (dom_sid, rid) = sd.split()#  SID   SID   RID ------- #....         - sam.add(ld). (  - ) #    : setpw = """ dn: %s changetype: modify replace: unicodePwd unicodePwd:: %s """ % (dn, base64.b64encode(str(r['unicodePwd']))) sam.transaction_start() try: sam.modify_ldif(setpw,["local_oid:1.3.6.1.4.1.7165.4.3.12:0"]) except: sam.transaction_cancel() print( '!!! ERROR SET PASSWORD USER : %s' % r['sAMAccountName']) else: sam.transaction_commit() 

Now a real example of transferring users from a domain under Win 2003 to Samba4.

There are problems in the old domain (starting even with the wrong domain name). Normal replication from DC to Samba4 (in the opposite direction - Samba4 DC -> W2003 DC) did not start up, probably due to intradomain problems.
The task was burdened by the presence of a file server on Samba3, so you had to save the sAMAccountName <-> mapping (UID, GID) that already exists in Samba3 (usually /var/lib/samba/winbindd_idmap.tdb ). Actually the task was similar to that described here .

All experiments and the final version were done on Ubuntu 14.04 servers running in OpenVZ containers (CentOS 6)
Installation, configuration Samba4 described many times. For example, already mentioned, here . For the normal display of the Unix ID in the scheme with rfc2307 sssd was used. By the way, the Samba4 build from sernet, which many recommend, is better not to use - it is difficult to make friends with the sssd package.

To save passwords and SID users, you must have the old database AD in the form of private directory Samba4, as mentioned above. Skipping details (see here ), “samba-tool domain join samdom.example.com DC -Uadministrator --realm = samdom.example.com” - you can stop at this without starting the samba service, since the necessary database has already been created . If it is necessary to further update the database, then the launch of the samba service is indispensable.

The impact on the existing Win AD domain is minimal (another controller is created, almost non-functional, so there will be many NTDS Replication errors in the logs), after creating an autonomous AD database, you can practice in virtual environments without any risk. If the MS Win domain needs to work normally for some time, it is better to kill this temporary Samba4 and clean the information about this DC from the working DC.

The resulting private directory (usually / var / lib / samba / private or / usr / local / samba / private ) must be copied somewhere to a future Samba4 and copy smb.conf from / etc / samba to the same place. Now all data on the old domain is stored in one place and is also available in the local filesystem.

If there is still a file server on Samba3, then somewhere alongside you should also put the / var / lib / samba directory from Samba3 (2 files are needed there - winbindd_idmap.tdb and group_mapping.tdb), if we want to keep the current idmap in Samba3 .

Initial parameters are designed as a conf.py file:

 #!/usr/bin/env python # -*- coding: utf-8 -*- smb_conf = '/etc/samba/smb.conf' smb_priv = '/var/lib/samba/private' dom0 = 'olddom.mydom.ru' #    dom1 = 'newdom.lan' #   . (!)  ,    -      host = 'newdc' #    maildom = 'mydom.ru' # .  #homeDirectory, homeDrive = r'\\newdc\Users','Z:' #  smb_priv0 = '/var/lib/samba/private-0' #private directory    samba4 (    smb.conf!) smb3db = '/var/lib/samba/samba3' #  -  3 ( /var/lib/samba ()),  None start_unix_id = 50000 #   GID  UID   . ############     d0, d1 = dom0.split('.'), dom1.split('.') base0 = ','.join(['DC='+x for x in d0]) base, dom, realm = ','.join(['DC='+x for x in d1]), d1[0].upper(), '.'.join(d1).upper() 

Let's collect the basic functions of transferring to one file
lib1.py
 #!/usr/bin/env python # -*- coding: utf-8 -*- import sys import string import ldb from struct import unpack #from samba.idmap import IDmapDB from samba.samdb import SamDB from samba.auth import system_session from samba.ndr import ndr_unpack from samba.dcerpc import security import samba.param import base64 from conf import * def get_1out(cmd): # 1     import subprocess return subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout.read().splitlines()[0] if not 'host' in globals(): host = get_1out('hostname').upper() if not 'smb3db' in globals(): smb3db = None my_log = lambda *x: None chgDc = lambda x: x def set_my_log(): #   -.   .   from datetime import datetime global my_log f_log = open(datetime.strftime(datetime.now(), "log_%y-%m-%d_%H:%M:%S.txt"),'w') def my_log(*x): try: xx = ' '.join(x) except: xx = ' '.join(map(lambda a: str(a),x)) f_log.write(xx+'\n') print xx def mk_chg(d0=d0,d1=d1): #  . d0,d1      . !!!: len(d0) >= len(d1) import re global chgDc if d0 == d1: return dif = len(d0)-len(d1)+1 #      , ddx = [d0[0],] + d0[dif:] #     ,  ddx == d0 #1  , 2   , 3 .. : myRe = [('',re.compile(r'\b(DC=)?%s\b[,.]?' % x, re.I)) for x in d0[1:dif]] +\ [(b,re.compile(r'\b%s\b' % a)) for (a,b) in zip(ddx,d1)] +\ [(b.upper(),re.compile(r'\b%s\b' % a.upper(), re.I)) for (a,b) in zip(ddx,d1)] def chg(s): #    d0 -> d1 for r in myRe: s = r[1].sub(r[0],s) return s chgDc = chg return chg def mk_sam0(smb_priv0=smb_priv0): #    . smb_priv0 - private directory   (    smb.conf!) import os global sam0 lp0 = samba.param.LoadParm() lp0.load(smb_priv0+'/smb.conf') # smb.conf     private directory!!! lp0.set('private directory',smb_priv0) sam0 = SamDB(lp=lp0,session_info=system_session()) os.unsetenv('SMB_CONF_PATH') return sam0 def mk_sam(host=host): #         global sam, ridSet, ridMan, sfu, idmap, minRid, nis lp = samba.param.LoadParm() lp.load(smb_conf) lp.set('private directory',smb_priv) sam = SamDB(lp=lp,session_info=system_session()) # idmap = IDmapDB(lp=lp) sfu = "CN=%s,CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System,%s" % (dom,base) # dn  sfu (msSFU30OrderNumber...) ridSet = "CN=RID Set,CN=%s,OU=Domain Controllers,%s" % (host, base) # dn  rIDNextRID ridMan = "CN=RID Manager$,CN=System,"+base # dn  RID Manager if not 'minRid' in globals(): minRid = int(sam.search(base = ridSet, attrs=["rIDNextRID"])[0]["rIDNextRID"][0]) my_log('\tminRid=%s' % str(minRid)) if not 'nis' in globals(): nis = str(sam.search(base=sfu, attrs=['msSFU30Domains'])[0]['msSFU30Domains'][0]) sam.nis = nis return sam def get_map0(i=None, path=smb3db, maps = {}): #  sAMAccountName <-> UID   idmap smb3 -  maps if not maps: if path != None: from samba.samba3 import DbDatabase mapdb = DbDatabase(path+'/group_mapping') for x in mapdb.db.iterkeys(): if x.startswith('UNIXGROUP') : y = mapdb.db.get(x) maps[y[8:-2]] = ('GID',unpack('<L',y[0:4])[0]) mapdb.close() mapdb = DbDatabase(path+'/winbindd_idmap') for x in mapdb.db.iterkeys(): if x.startswith('S-1'): y = mapdb.db.get(x) res = sam0.search(base=base0,expression="(objectSid=%s)" % x.rstrip("\x00"), scope=ldb.SCOPE_SUBTREE, attrs=["sAMAccountName"]) if len(res) > 0 and 'sAMAccountName' in res[0]: maps[str(res[0]["sAMAccountName"][0])] = y.rstrip("\x00").split(" ") mapdb.close() else: #     idmap,       ID 'Domain Users' my_log('!? Not MAP0') maps['Domain Users'] = ['GID',start_unix_id] if 'sam' in globals(): res = sam.search(base=base,expression="(sAMAccountName=Domain Users)",attrs=['gidNumber']) if len(res) > 0 and 'gidNumber' in res[0]: maps['Domain Users'][1] = int(res[0]['gidNumber'][0]) maps['_users'] = maps['Domain Users'][1] # .    my_log('Set Domain Users = %d' % maps['_users']) maps['Administrator'] = ('UID',0) maps['Administrators'] = ('GID',0) if i == None: return maps return maps[i] if i in maps else False def mk_fill_matrix(m,r): # helper    m  cp_usr()  cp_grp() def rp(k,chg=0): #  chg!=0 -   ! if k in r: m[k] = str(r[k][0]) if chg==0 else chgDc(str(r[k][0])) return rp def mk_fill_ldb_msg(dn): # helper      ldb.Message m2 m2 = ldb.Message() m2.dn = ldb.Dn(sam, str(dn)) def rp(fld=None,val='',flg=ldb.FLAG_MOD_REPLACE): if fld: m2[fld] = ldb.MessageElement(str(val), flg, fld) return m2 return rp def usn_sort(res): #    res    -     (ou, grp) x = [r for r in res] x.sort(key = lambda r: int(r["uSNCreated"][0])) return x def rid_sort(res): #    res   RID x = [r for r in res] x.sort(key = lambda r: int(unpack('<I',r['objectSid'][0][-4:])[0])) return x def set_grp_gid(r,gid): # Unix GID   rp = mk_fill_ldb_msg(r.dn) rp("msSFU30NisDomain",nis) rp("msSFU30Name",r['sAMAccountName'][0]) rp("gidNumber",gid) sam.modify(rp()) def set_usr_gid_uid(r,uid): # Unix GID, UID   rp = mk_fill_ldb_msg(r.dn) rp("msSFU30NisDomain",nis) rp("uid",r['sAMAccountName'][0]) rp("uidNumber",uid) rp("gidNumber",get_map0('_users')) # rp('objectClass','posixAccount',ldb.FLAG_MOD_ADD) sam.modify(rp()) def map_grp(): #   GID  IdMap  Samba 3 res = sam.search(base=base,expression="(objectClass=group)") my_log( "\tmap_grp ALL GRP COUNT: %s" % len(res)) sam.transaction_start() try: for r in res: x = get_map0(str(r['sAMAccountName'][0])) if x: set_grp_gid(r,x[1]) except: sam.transaction_cancel() my_log( '!!! ERROR MAP GRP %s' % r['sAMAccountName']) else: sam.transaction_commit() def map_usr(): #   UID  IdMap  Samba 3 res = sam.search(base=base,expression="(&(objectCategory=person)(objectClass=user))",attrs=["sAMAccountName"]) my_log( "\tmap_usr ALL USR COUNT: %s" % len(res)) sam.transaction_start() try: for r in res: x = get_map0(str(r['sAMAccountName'][0])) if x: set_usr_gid_uid(r,x[1]) except: sam.transaction_cancel() my_log( '!!! ERROR SET SFU30 ATTR. USER %s !!!' % x[1]) else: sam.transaction_commit() def cp_ou(): #  organizationalUnit ous = [str(r.dn) for r in sam.search(base=base,expression="(objectClass=organizationalUnit)", attrs=[])] res = sam0.search(base=base0,expression="(objectClass=organizationalUnit)") my_log( "\tOU COUNT: %s" % len(res)) for r in usn_sort(res): dn = chgDc(str(r.dn)) if not dn in ous: m = {"dn": dn, "objectClass": "organizationalUnit", "name": str(r["name"][0])} try: sam.add(m) except: my_log("!!!Error Add OU : %s" % dn) def cp_usr(): #   users = [str(r.dn) for r in sam.search(base=base,expression="(&(objectCategory=person)(objectClass=user))", attrs=[])] res = sam0.search(base=base0,expression="(&(objectCategory=person)(objectClass=user))", attrs=['*','unicodePwd']) my_log( "\tNEW USR COUNT: %s" % len(res)) for r in res: dn = chgDc(str(r.dn)) if dn in users: continue sd = ndr_unpack(security.dom_sid,r['objectSid'][0]) (group_dom_sid, rid) = sd.split() if rid <= minRid: my_log( '!! MinRid ERR : ', r['sAMAccountName'][0] ) continue m = {"dn": dn, "objectClass": "user"} rp = mk_fill_matrix(m,r) rp('userPrincipalName',1) rp('sAMAccountName') rp('sn') rp('name') rp('initials') rp('displayName') rp('scriptPath') rp('description') rp('userAccountControl') rp('pwdLastSet') m["nTSecurityDescriptor"] = r['objectSid'] if 'maildom' in globals(): m["mail"] = "%s@%s" % (r['sAMAccountName'][0], maildom) if 'homeDirectory' in globals(): m["homeDirectory"] = r"%s\%s" % (homeDirectory,r['sAMAccountName'][0]) if 'homeDrive' in globals(): m["homeDrive"] = homeDrive sam.transaction_start() try: sam.add(m) except: sam.transaction_cancel() my_log( '!!! ERROR ADD USER : %s' % m['sAMAccountName']) else: sam.transaction_commit() # Copy the password for it if not 'unicodePwd' in r: my_log( '!!! NOT PASSWD FOR USER : %s' % m['sAMAccountName']) continue setpw = """ dn: %s changetype: modify replace: unicodePwd unicodePwd:: %s """ % (dn, base64.b64encode(str(r['unicodePwd']))) sam.transaction_start() try: sam.modify_ldif(setpw,["local_oid:1.3.6.1.4.1.7165.4.3.12:0"]) except: sam.transaction_cancel() my_log( '!!! ERROR SET PASSWORD USER : %s' % m['sAMAccountName']) else: sam.transaction_commit() def cp_grp(): #   grps = [str(r.dn) for r in sam.search(base=base,expression="(objectClass=group)", attrs=[])] res = sam0.search(base=base0,expression="(&(objectClass=group)(objectCategory=Group))") my_log( "\tNEW GRP COUNT: %s" % len(res)) for r in usn_sort(res): dn = chgDc(str(r.dn)) if dn in grps: continue sd = ndr_unpack(security.dom_sid,r['objectSid'][0]) (group_dom_sid, rid) = sd.split() if rid <= minRid: my_log( '!! MinRid ERR : ', r['name'][0] ) continue m = {"dn": dn, "objectClass": "group"} rp = mk_fill_matrix(m,r) rp('sAMAccountName') rp('groupType') rp('description') m["nTSecurityDescriptor"] = r['objectSid'] sam.transaction_start() try: sam.add(m) except: sam.transaction_cancel() my_log( '!!! ERROR add GRP %s !!!' % m['sAMAccountName']) else: sam.transaction_commit() def grp_fill(): #   my_log( "\tgrp_fill") grps ={} for r in sam.search(base=base,expression="(&(objectClass=group)(objectCategory=Group))",attrs=['member']): grps[str(r.dn)] = r['member'] if 'member' in r else [] users = [str(r.dn) for r in sam.search(base=base,expression="(&(objectCategory=person)(objectClass=user))", attrs=[])] for r in sam0.search(base=base0,expression="(&(objectClass=group)(objectCategory=Group))",attrs=['member']): if not 'member' in r: continue grp = chgDc(str(r.dn)) if not grps.has_key(grp): my_log( "!!not found group:\t",grp) continue add_m = '' for m in r['member']: m = chgDc(m) if m in grps[grp]: continue if not m in users: try: sam.search(base=m, attrs=[]) except: my_log( "!? err (not found) add %s \tto %s" % (m,grp)) continue add_m += "add: member\nmember: %s\n" % (m) if add_m == '': continue add_m = "\ndn: %s\nchangetype: modify\n%s\n" % (grp,add_m) sam.transaction_start() try: sam.modify_ldif(add_m) except: sam.transaction_cancel() my_log( "!!!Error fill grp "+grp) else: sam.transaction_commit() def set_max_gid_uid(max_gid=start_unix_id, max_uid=start_unix_id): #  max GID, UID   my_log('set_max_gid_uid start: set max_gid=%s, max_uid=%s' % (max_gid, max_uid)) chg = '' r = sam.search(base=sfu)[0] for x in (['msSFU30MaxGidNumber',max_gid],['msSFU30MaxUidNumber',max_uid]): x[1] = max(x[1],start_unix_id) if not x[0] in r or x[1] > int(r[x[0]][0]): chg += "replace: %s\n%s: %d\n" % (x[0],x[0],x[1]) if chg != '': chg = "\ndn: %s\nchangetype: modify\n%s\n" % (sfu,chg) sam.transaction_start() try: sam.modify_ldif(chg) except: sam.transaction_cancel() my_log( "!!!Error set msSFU30Max...") else: sam.transaction_commit() def get_next_uid(): #   UID   nm = 'msSFU30MaxUidNumber' r = sam.search(base=sfu)[0] x = start_unix_id if nm in r: x = max(x, int(r[nm][0])) sam.transaction_start() try: sam.modify_ldif("\ndn: %s\nchangetype: modify\nreplace: %s\n%s: %d\n" % (sfu,nm,nm,x+1)) except: sam.transaction_cancel() my_log( "!!!Error set msSFU30Max...") raise else: sam.transaction_commit() return x def set_max_id(): # max GID, UID,   ,   +1 max_gid = max([int(r['gidNumber'][0]) for r in sam.search(base=base,expression="(&(objectClass=group)(gidNumber=*))",attrs=['gidNumber'])]+[0]) max_uid = max([int(r['uidNumber'][0]) for r in sam.search(base=base,expression="(&(objectCategory=person)(objectClass=user)(uidNumber=*))",attrs=['uidNumber'])]+[0]) set_max_gid_uid(max_gid=max_gid+1,max_uid=max_uid+1) def check_id(): # . GID, UID   r = sam.search(base=sfu)[0] (max_gid,max_uid) = [int(r[x][0]) if x in r else start_unix_id for x in ('msSFU30MaxGidNumber','msSFU30MaxUidNumber')] my_log('check_id start: initial max_gid=%d, max_uid=%d' % (max_gid, max_uid)) sam.transaction_start() try: for r in rid_sort(sam.search(base=base,expression="(&(objectClass=group)(!(gidNumber=*)))",attrs=['sAMAccountName','objectSid'])): set_grp_gid(r,max_gid) max_gid += 1 for r in usn_sort(sam.search(base=base,expression="(&(objectCategory=person)(objectClass=user)(!(uidNumber=*)))",attrs=['sAMAccountName','uSNCreated'])): set_usr_gid_uid(r,max_uid) max_uid += 1 except: sam.transaction_cancel() my_log( '!!! ERROR check_id %s' % r['sAMAccountName']) else: sam.transaction_commit() set_max_gid_uid(max_gid=max_gid,max_uid=max_uid) def set_max_rid(): # max RID  ,   "RID Set"  "RID Manager" res = sam.search(base=base,expression="(&(sAMAccountName=*)(objectSid=*))",attrs=["objectSid"]) my_log( "\tSID COUNT: %s" % len(res)) x = max([int(unpack('<I',r['objectSid'][0][-4:])[0]) for r in res]) rmin = (x-100)/500*500+100 pool = rmin + ((rmin + 499) << 32) my_log("\tRid Set: %d %d %d " % (rmin,x,rmin + 499)) m = mk_fill_ldb_msg(ridSet) m('rIDNextRID',x) m('rIDAllocationPool',pool) m('rIDPreviousAllocationPool',pool) m2 = mk_fill_ldb_msg(ridMan) m2('rIDAvailablePool',rmin + 500 + (1073741823 << 32)) sam.transaction_start() try: sam.modify(m()) sam.modify(m2()) except: sam.transaction_cancel() my_log( '!!! ERROR Set rIDNextRID %s' % x) else: sam.transaction_commit() return x def mk_dom(): #     samba 4   SID,       administrator = "1" !!! from samba.netcmd.main import cmd_sambatool def cmd(args, subcom='domain'): cmd = cmd_sambatool() try: retval = cmd._run("samba-tool", subcom, *args) except SystemExit, e: retval = e.code except Exception, e: cmd.show_command_error(e) retval = 1 if retval: sys.exit(retval) cmd(('provision', '--host-name=%s' % host, '--realm=%s' % realm, '--domain=%s' % dom, '--domain-sid=%s' % get_1out('./get_dom_sid.py'), '--adminpass=UJHkjhm7KH$$2vrXy', '--function-level=2003', '--server-role=dc', '--use-rfc2307', '--dns-backend=SAMBA_INTERNAL')) cmd(('passwordsettings', 'set', '--complexity=off', '--history-length=0', '--min-pwd-length=0', '--min-pwd-age=0', '--max-pwd-age=0')) cmd(('setpassword', 'administrator', '--newpassword=1'),subcom='user') #    1.     !! 

Here there is a call to get_dom_sid.py as an external program in the mk_dom () function - initialization of a new domain.
get_dom_sid.py just prints the old domain sid:

 #!/usr/bin/env python from lib1 import mk_sam0 print mk_sam0().domain_sid 

So I had to do this, because when connecting to the database of the old domain in the same stream as creating the new one, the environment variables were replaced with old data.


So, after all the necessary packages are installed (samba4, sssd and dependencies), the directories of the old databases are copied to the right places, we can start creating a new domain.

Domain initialization - start mk_dom.py:

 #!/usr/bin/env python # -*- coding: utf-8 -*- from lib1 import * set_my_log() mk_dom() 

If everything went well (the log file is named log_% y-% m-% d_% H:% M:% S.txt) we look at smb.conf, temporarily add it to the [global] section
dns forwarder = <address of the old DC> (at the time of transferring workstations to a new domain).

Copy krb5.conf from / var / lib / samba / private to / etc (or make a symbolic link). Next, run the script to copy objects from the old domain cp_dom.py:

 #!/usr/bin/env python # -*- coding: utf-8 -*- from lib1 import * set_my_log() my_log(base0,' ->',base, dom, realm) mk_chg() #    mk_sam0() #    mk_sam() #    cp_ou() # organizationalUnit cp_grp() #  cp_usr() #  grp_fill() #   get_map0() #   sAMAccountName <-> UID (   idmap Samba3) map_grp() # unix GID   map_usr() # unix GID, UID   set_max_rid() # max RID  ,   "RID Set"  "RID Manager" set_max_id() # max GID, UID,   ,   +1   check_id() #  GID, UID   

Inevitably, there are errors in the log file. The most serious - with three signs "!" in front. Type:
!!! ERROR ADD USER: 5CA6ADDF-A2C8-46E5-A
!!! ERROR SET PASSWORD USER: 5CA6ADDF-A2C8-46E5-A
Most often these are objects that are missing in the current scheme and are essentially unnecessary. If there were users in the domain from another domain of the forest, adding them will not work either and will get into the log. Type errors are irrelevant !! MinRid ERR: DCOM Users

If everything is tolerable, start Samba:

 start samba-ad-dc 

A little bit about sssd settings
Described in detail here (Method 1: Connecting to AD via Kerberos).
Cooking kerberos for sssd:

 samba-tool domain exportkeytab /etc/krb5.sssd.keytab --principal=<myHostName>$ chown root:root /etc/krb5.sssd.keytab chmod 600 /etc/krb5.sssd.keytab 

File /etc/sssd/sssd.conf:

 [sssd] services = nss, pam config_file_version = 2 domains = newdom.lan [nss] [pam] [domain/newdom.lan] id_provider = ad auth_provider = ad ldap_schema = ad krb5_keytab = /etc/krb5.sssd.keytab access_provider = ad ldap_id_mapping=false enumerate = true 

Reset sssd cache:

 sss_cache -GU 

Restart sssd:

 restart sssd 

By the way, resetting the sssd cache may not help with large AD changes. Then, with sssd stopped, remove the directory from / var / lib / sss / and restore their empty structure (from the installation package).

We check the display of users and groups (the sssd database is filled for a while):

 getent passwd getent group 

The easiest way to drag users is the netdom.exe utility (netdom.exe move /?), Adding it to the logon script on the old server. Only it is necessary to start the version of netdom.exe suitable for OS. Since the SID, GID, UID, and user passwords are saved, the movement is almost transparent to users — local folders remain with them, network resources too. You just need to rename the domain in the Samba file server configuration.

It was even simpler for me - since all this zoo lived under OpenVZ, the network resource on a separate file system was easily mounted to different file servers at the same time (you can also make DC a file server - normal), and access problems were automatically resolved by matching GID and UID.

Group Policy objects were not transferred to the new domain.

Source: https://habr.com/ru/post/259029/


All Articles