1 # domain management - domain provision
3 # Copyright Matthias Dieter Wallnoefer 2009
4 # Copyright Andrew Kroeger 2009
5 # Copyright Jelmer Vernooij 2007-2012
6 # Copyright Giampaolo Lauria 2011
7 # Copyright Matthieu Patou <mat@matws.net> 2011
8 # Copyright Andrew Bartlett 2008-2015
9 # Copyright Stefan Metzmacher 2012
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
30 import samba
.getopt
as options
31 from samba
.auth
import system_session
32 from samba
.auth_util
import system_session_unix
33 from samba
.dcerpc
import security
34 from samba
.dsdb
import (
35 DS_DOMAIN_FUNCTION_2000
,
36 DS_DOMAIN_FUNCTION_2003
,
37 DS_DOMAIN_FUNCTION_2008
,
38 DS_DOMAIN_FUNCTION_2008_R2
,
39 DS_DOMAIN_FUNCTION_2012
,
40 DS_DOMAIN_FUNCTION_2012_R2
,
41 DS_DOMAIN_FUNCTION_2016
43 from samba
.netcmd
import Command
, CommandError
, Option
44 from samba
.provision
import DEFAULT_MIN_PWD_LENGTH
, ProvisioningError
, provision
45 from samba
.provision
.common
import FILL_DRS
, FILL_FULL
, FILL_NT4SYNC
46 from samba
.samdb
import get_default_backend_store
47 from samba
import functional_level
49 from .common
import common_ntvfs_options
, common_provision_join_options
52 class cmd_domain_provision(Command
):
53 """Provision a domain."""
55 synopsis
= "%prog [options]"
57 takes_optiongroups
= {
58 "sambaopts": options
.SambaOptions
,
59 "versionopts": options
.VersionOptions
,
63 Option("--interactive", help="Ask for names", action
="store_true"),
64 Option("--domain", type="string", metavar
="DOMAIN",
65 help="NetBIOS domain name to use"),
66 Option("--domain-guid", type="string", metavar
="GUID",
67 help="set domainguid (otherwise random)"),
68 Option("--domain-sid", type="string", metavar
="SID",
69 help="set domainsid (otherwise random)"),
70 Option("--ntds-guid", type="string", metavar
="GUID",
71 help="set NTDS object GUID (otherwise random)"),
72 Option("--invocationid", type="string", metavar
="GUID",
73 help="set invocationid (otherwise random)"),
74 Option("--host-name", type="string", metavar
="HOSTNAME",
76 Option("--host-ip", type="string", metavar
="IPADDRESS",
77 help="set IPv4 ipaddress"),
78 Option("--host-ip6", type="string", metavar
="IP6ADDRESS",
79 help="set IPv6 ipaddress"),
80 Option("--site", type="string", metavar
="SITENAME",
81 help="set site name"),
82 Option("--adminpass", type="string", metavar
="PASSWORD",
83 help="choose admin password (otherwise random)"),
84 Option("--krbtgtpass", type="string", metavar
="PASSWORD",
85 help="choose krbtgt password (otherwise random)"),
86 Option("--dns-backend", type="choice", metavar
="NAMESERVER-BACKEND",
87 choices
=["SAMBA_INTERNAL", "BIND9_FLATFILE", "BIND9_DLZ", "NONE"],
88 help="The DNS server backend. SAMBA_INTERNAL is the builtin name server (default), "
89 "BIND9_FLATFILE uses bind9 text database to store zone information, "
90 "BIND9_DLZ uses samba4 AD to store zone information, "
91 "NONE skips the DNS setup entirely (not recommended)",
92 default
="SAMBA_INTERNAL"),
93 Option("--dnspass", type="string", metavar
="PASSWORD",
94 help="choose dns password (otherwise random)"),
95 Option("--root", type="string", metavar
="USERNAME",
96 help="choose 'root' unix username"),
97 Option("--nobody", type="string", metavar
="USERNAME",
98 help="choose 'nobody' user"),
99 Option("--users", type="string", metavar
="GROUPNAME",
100 help="choose 'users' group"),
101 Option("--blank", action
="store_true",
102 help="do not add users or groups, just the structure"),
103 Option("--server-role", type="choice", metavar
="ROLE",
104 choices
=["domain controller", "dc", "member server", "member", "standalone"],
105 help="The server role (domain controller | dc | member server | member | standalone). Default is dc.",
106 default
="domain controller"),
107 Option("--function-level", type="choice", metavar
="FOR-FUN-LEVEL",
108 choices
=["2000", "2003", "2008", "2008_R2", "2016"],
109 help="The domain and forest function level (2000 | 2003 | 2008 | 2008_R2 - always native | 2016). Default is (Windows) 2008_R2 Native.",
111 Option("--base-schema", type="choice", metavar
="BASE-SCHEMA",
112 choices
=["2008_R2", "2008_R2_old", "2012", "2012_R2", "2016", "2019"],
113 help="The base schema files to use. Default is (Windows) 2019.",
115 Option("--adprep-level", type="choice", metavar
="FUNCTION_LEVEL",
116 choices
=["SKIP", "2008_R2", "2012", "2012_R2", "2016"],
117 help="The highest functional level to prepare for. Default is based on --base-schema",
119 Option("--next-rid", type="int", metavar
="NEXTRID", default
=1000,
120 help="The initial nextRid value (only needed for upgrades). Default is 1000."),
121 Option("--partitions-only",
122 help="Configure Samba's partitions, but do not modify them (ie, join a BDC)", action
="store_true"),
123 Option("--use-rfc2307", action
="store_true", help="Use AD to store posix attributes (default = no)"),
127 Option("--use-xattrs", type="choice", choices
=["yes", "no", "auto"],
128 metavar
="[yes|no|auto]",
129 help="Define if we should use the native fs capabilities or a tdb file for "
130 "storing attributes likes ntacl when --use-ntvfs is set. "
131 "auto tries to make an intelligent guess based on the user rights and system capabilities",
135 takes_options
.extend(common_provision_join_options
)
137 if samba
.is_ntvfs_fileserver_built():
138 takes_options
.extend(common_ntvfs_options
)
139 takes_options
.extend(ntvfs_options
)
143 def run(self
, sambaopts
=None, versionopts
=None,
170 partitions_only
=None,
176 plaintext_secrets
=False,
178 backend_store_size
=None):
180 self
.logger
= self
.get_logger(name
="provision", quiet
=quiet
)
182 lp
= sambaopts
.get_loadparm()
183 smbconf
= lp
.configfile
185 if dns_forwarder
is not None:
186 suggested_forwarder
= dns_forwarder
188 suggested_forwarder
= self
._get
_nameserver
_ip
()
189 if suggested_forwarder
is None:
190 suggested_forwarder
= "none"
192 if not self
.raw_argv
:
196 from getpass
import getpass
199 def ask(prompt
, default
=None):
200 if default
is not None:
201 print("%s [%s]: " % (prompt
, default
), end
=' ')
203 print("%s: " % (prompt
,), end
=' ')
205 return sys
.stdin
.readline().rstrip("\n") or default
208 default
= socket
.getfqdn().split(".", 1)[1].upper()
211 realm
= ask("Realm", default
)
212 if realm
in (None, ""):
213 raise CommandError("No realm set!")
216 default
= realm
.split(".")[0]
219 domain
= ask("Domain", default
)
221 raise CommandError("No domain set!")
223 server_role
= ask("Server Role (dc, member, standalone)", "dc")
225 dns_backend
= ask("DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE)", "SAMBA_INTERNAL")
226 if dns_backend
in (None, ''):
227 raise CommandError("No DNS backend set!")
229 if dns_backend
== "SAMBA_INTERNAL":
230 dns_forwarder
= ask("DNS forwarder IP address (write 'none' to disable forwarding)", suggested_forwarder
)
231 if dns_forwarder
.lower() in (None, 'none'):
232 suggested_forwarder
= None
236 adminpassplain
= getpass("Administrator password: ")
237 issue
= self
._adminpass
_issue
(adminpassplain
)
239 self
.errf
.write("%s.\n" % issue
)
241 adminpassverify
= getpass("Retype password: ")
242 if not adminpassplain
== adminpassverify
:
243 self
.errf
.write("Sorry, passwords do not match.\n")
245 adminpass
= adminpassplain
249 realm
= sambaopts
._lp
.get('realm')
251 raise CommandError("No realm set!")
253 raise CommandError("No domain set!")
256 issue
= self
._adminpass
_issue
(adminpass
)
258 raise CommandError(issue
)
260 self
.logger
.info("Administrator password will be set randomly!")
263 dom_for_fun_level
= functional_level
.string_to_level(function_level
)
265 raise CommandError(f
"'{function_level}' is not a valid domain level")
267 if adprep_level
is None:
268 # Select the adprep_level default based
269 # on what the base schema permits
270 if base_schema
in ["2008_R2", "2008_R2_old"]:
271 # without explicit --adprep-level=2008_R2
272 # we will skip the adprep step on
274 adprep_level
= "SKIP"
275 elif base_schema
in ["2012"]:
276 adprep_level
= "2012"
277 elif base_schema
in ["2012_R2"]:
278 adprep_level
= "2012_R2"
280 adprep_level
= "2016"
282 if adprep_level
== "SKIP":
283 provision_adprep_level
= None
284 elif adprep_level
== "2008R2":
285 provision_adprep_level
= DS_DOMAIN_FUNCTION_2008_R2
286 elif adprep_level
== "2012":
287 provision_adprep_level
= DS_DOMAIN_FUNCTION_2012
288 elif adprep_level
== "2012_R2":
289 provision_adprep_level
= DS_DOMAIN_FUNCTION_2012_R2
290 elif adprep_level
== "2016":
291 provision_adprep_level
= DS_DOMAIN_FUNCTION_2016
293 if dns_backend
== "SAMBA_INTERNAL" and dns_forwarder
is None:
294 dns_forwarder
= suggested_forwarder
296 samdb_fill
= FILL_FULL
298 samdb_fill
= FILL_NT4SYNC
299 elif partitions_only
:
300 samdb_fill
= FILL_DRS
302 if targetdir
is not None:
303 if not os
.path
.isdir(targetdir
):
304 os
.makedirs(targetdir
)
308 if use_xattrs
== "yes":
310 elif use_xattrs
== "auto" and not use_ntvfs
:
313 raise CommandError("--use-xattrs=no requires --use-ntvfs (not supported for production use). "
314 "Please re-run with --use-xattrs omitted.")
315 elif use_xattrs
== "auto" and not lp
.get("posix:eadb"):
317 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(targetdir
))
319 file = tempfile
.NamedTemporaryFile(dir=os
.path
.abspath(os
.path
.dirname(lp
.get("private dir"))))
322 samba
.ntacls
.setntacl(lp
, file.name
,
323 "O:S-1-5-32G:S-1-5-32",
325 system_session_unix(),
329 self
.logger
.info("You are not root or your system does not support xattr, using tdb backend for attributes. ")
334 self
.logger
.info("not using extended attributes to store ACLs and other metadata. If you intend to use this provision in production, rerun the script as root on a system supporting xattrs.")
336 if domain_sid
is not None:
337 domain_sid
= security
.dom_sid(domain_sid
)
339 session
= system_session()
340 if backend_store
is None:
341 backend_store
= get_default_backend_store()
343 result
= provision(self
.logger
,
344 session
, smbconf
=smbconf
, targetdir
=targetdir
,
345 samdb_fill
=samdb_fill
, realm
=realm
, domain
=domain
,
346 domainguid
=domain_guid
, domainsid
=domain_sid
,
348 hostip
=host_ip
, hostip6
=host_ip6
,
349 sitename
=site
, ntdsguid
=ntds_guid
,
350 invocationid
=invocationid
, adminpass
=adminpass
,
351 krbtgtpass
=krbtgtpass
, machinepass
=machinepass
,
352 dns_backend
=dns_backend
, dns_forwarder
=dns_forwarder
,
353 dnspass
=dnspass
, root
=root
, nobody
=nobody
,
355 serverrole
=server_role
, dom_for_fun_level
=dom_for_fun_level
,
356 useeadb
=eadb
, next_rid
=next_rid
, lp
=lp
, use_ntvfs
=use_ntvfs
,
357 use_rfc2307
=use_rfc2307
, skip_sysvolacl
=False,
358 base_schema
=base_schema
,
359 adprep_level
=provision_adprep_level
,
360 plaintext_secrets
=plaintext_secrets
,
361 backend_store
=backend_store
,
362 backend_store_size
=backend_store_size
)
364 except ProvisioningError
as e
:
365 raise CommandError("Provision failed", e
)
367 result
.report_logger(self
.logger
)
369 def _get_nameserver_ip(self
):
370 """Grab the nameserver IP address from /etc/resolv.conf."""
372 RESOLV_CONF
= "/etc/resolv.conf"
374 if not path
.isfile(RESOLV_CONF
):
375 self
.logger
.warning("Failed to locate %s" % RESOLV_CONF
)
380 handle
= open(RESOLV_CONF
, 'r')
382 if not line
.startswith('nameserver'):
384 # we want the last non-space continuous string of the line
385 return line
.strip().split()[-1]
387 if handle
is not None:
390 self
.logger
.warning("No nameserver found in %s" % RESOLV_CONF
)
392 def _adminpass_issue(self
, adminpass
):
393 """Returns error string for a bad administrator password,
394 or None if acceptable"""
395 if isinstance(adminpass
, bytes
):
396 adminpass
= adminpass
.decode('utf8')
397 if len(adminpass
) < DEFAULT_MIN_PWD_LENGTH
:
398 return "Administrator password does not meet the default minimum" \
399 " password length requirement (%d characters)" \
400 % DEFAULT_MIN_PWD_LENGTH
401 elif not samba
.check_password_quality(adminpass
):
402 return "Administrator password does not meet the default" \