firstuser.in 7.61 KB
Newer Older
1
#!/usr/bin/perl -w
Leigh B. Stoller's avatar
Leigh B. Stoller committed
2 3
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2000-2007 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
5 6
# All rights reserved.
#
Leigh B. Stoller's avatar
Leigh B. Stoller committed
7 8 9
#
# WARNING: See db/genelists.in for clause testing for protouser names.
#
10
use English;
11
use strict;
12
use Getopt::Std;
13 14 15

use lib '@prefix@/lib';
use libdb;
16
use libtestbed;
17
use User;
18

19 20
my $tbadmin    = '@TBADMINGROUP@';
my $ELABINELAB = @ELABINELAB@;
21 22
my $MINUID     = @MIN_UNIX_UID@;
my $MINGID     = @MIN_UNIX_GID@;
23

24 25 26
my $wap     = '@prefix@/sbin/withadminprivs';
my $mkproj  = '@prefix@/sbin/mkproj';
my $mkgroup = '@prefix@/sbin/mkgroup';
27
my $mkacct  = '@prefix@/sbin/tbacct';
28
my $modgrps = '@prefix@/sbin/modgroups';
29

30 31 32
my $protouser       = 'elabman';
my $protouser_name  = 'Emulab Manager';
my $protouser_email = '@TBOPSEMAIL@';
33
my $protouser_shell = 'tcsh';
34
my $protouser_notes = "DO NOT DELETE THIS ACCOUNT!";
35
my $HOMEDIR         = USERROOT();
36 37
my $protoproj       = 'emulab-ops';
my $protoproj_desc  = 'Operations Meta-Project';
38
my $batchmode       = 0;
39
my $pid_idx         = 1; # Initial IDX for protoproj.
Timothy Stack's avatar
 
Timothy Stack committed
40
my $trust           = "project_root";
41
my $binshell        = "/bin/nologin";
42 43
my $uid_uuid;
my $gid_uuid;
44
my $password;
45
my $encpass;
46
my %opts;
47
my $mailman_password;
48

Timothy Stack's avatar
 
Timothy Stack committed
49 50 51
my $CONTROL	= "@USERNODE@";
my $BOSSNODE	= "@BOSSNODE@";

52 53 54 55
#
# Handle command-line options
#
sub usage {
Timothy Stack's avatar
 
Timothy Stack committed
56
    print "Usage: firstuser [-b] [-p password] [-u user] [-n name] [-e email]\n";
57 58
    exit(1);
}
Timothy Stack's avatar
 
Timothy Stack committed
59
if (! getopts("bp:u:n:e:", \%opts)) {
60 61 62 63 64 65 66 67
    usage();
}
if (defined($opts{b})) {
    $batchmode = 1;
}
if (defined($opts{p})) {
    $password = $opts{p};
}
Timothy Stack's avatar
 
Timothy Stack committed
68 69 70 71 72 73 74 75 76 77
if (defined($opts{u})) {
    $protouser = $opts{u};
    $trust = "local_root";
}
if (defined($opts{n})) {
    $protouser_name = $opts{n};
}
if (defined($opts{e})) {
    $protouser_email = $opts{e};
}
78

79 80 81
my $user = User->Lookup('$protouser');
if (defined($user)) {
    die("This script has already been run, no need to run it again!\n");
82 83
}

84 85
if ($UID != 0) {
    die "Sorry, this must be run as root\n";
86 87
}

Timothy Stack's avatar
 
Timothy Stack committed
88 89 90 91 92
if (!defined($opts{u})) {
    print "This script will create the 'proto-user' $protouser, which you will\n";
    print "use to bootstrap other users. It also creates the emulab-ops\n";
    print "meta-project.\n\n";
}
93

94 95 96
# Need this for mailman support; harmless.
$mailman_password = substr(TBGenSecretKey(), 0, 10);

97
# Get a password for the user
98
if (!defined($password)) {
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
    #
    # In batch mode, we get it from the root user password entry.
    # Otherwise prompt user for it.
    #
    if ($batchmode) {
	(undef,$encpass) = getpwnam("root") or
	    die("No root user!\n");
    }
    else {
	print "Pick a password for $protouser (warning, will be echoed): ";
	$password = <>;
	chomp($password);
    }
}
if (!defined($encpass)) {
    my @salt_chars = ('a'..'z','A'..'Z','0'..'9');
    my $salt = $salt_chars[rand(@salt_chars)] .
	       $salt_chars[rand(@salt_chars)];
    $encpass = crypt($password, "\$1\$${salt}");
118
}
119

120
# Get uid for the user and a gid for the project
121 122

# For non-initial users, get the next available index.
123
my $result = DBQueryFatal("select idx from emulab_indicies ".
124 125 126 127 128
		       "where name='next_uid'");
if ($result->numrows() > 0) {
    ($MINUID) = $result->fetchrow_array();
}

129
my $uid = $MINUID;
130
while (getpwuid($uid)) { $uid++; }
131

132
my $gid = $MINGID;
133 134
while (getgrgid($gid)) { $gid++; }

135 136 137 138 139
# We put the proto-user in the tbadmin group, because the emulab-ops
# group does not exist yet
my $agid = (getgrnam($tbadmin))[2];
if (!defined $agid) {
    die "Unable to get group ID for $tbadmin\n";
140
}
141 142
# And in the wheel group so elabman can sudo to root.
my $Ggid = "wheel";
Timothy Stack's avatar
 
Timothy Stack committed
143 144 145 146

if (defined($opts{u})) {
    $Ggid .= " $protoproj";
}
147
    
148 149 150 151 152
if (!$batchmode) {
    print "Creating user/project: Are you sure? (Y/N) ";
    if (<> !~ /Y/i) {
	die "Aborted\n";
    }
153 154
}

155 156 157 158 159 160 161 162
# Initial protouser gets a real shell until actively frozen later.
# Also setup a notes entry.
if (!defined($opts{u})) {
    $binshell = "/bin/tcsh";
    $protouser_notes = "This account can be frozen after your Emulab ".
	"is fully setup and running. DO NOT DELETE THIS ACCOUNT!";
}

163
print "Creating user on boss...\n";
164 165 166 167 168
if (system("/usr/sbin/pw useradd $protouser -u $uid -g $agid ".
	   "-G \"$Ggid\" -h - " .
	   "-m -d $HOMEDIR/$protouser -s $binshell ".
	   "-c \"$protouser_name\"")) {
    die "Unable to add user to the password file!\n";
169 170
}

Timothy Stack's avatar
 
Timothy Stack committed
171 172 173 174 175
if ($CONTROL ne $BOSSNODE) {
    print "Creating user on ops...\n";
    
    if (system("ssh $CONTROL ".
	       "'/usr/sbin/pw useradd $protouser -u $uid -g $agid ".
176
	       "-G \"$Ggid\" -h - -d $HOMEDIR/$protouser -s $binshell ".
Timothy Stack's avatar
 
Timothy Stack committed
177 178 179 180 181
	       "-c \"$protouser_name\"'")) {
	die "Unable to add user to the ops password file!\n";
    }
}

182 183 184
# These users do not really need a uuid, but give them one anyway.
$uid_uuid = NewUUID();

185
# Initialize the index value;
186
DBQueryFatal("replace into emulab_indicies set name='next_uid',idx=$uid+1");
187

188
print "Creating user in database...\n";
189
DBQueryFatal("insert into users set uid='$protouser', usr_created=now(), " .
190
	     "usr_name='$protouser_name', uid_uuid='$uid_uuid', ".
191 192 193 194 195
	     "usr_addr='DO NOT DELETE THIS ACCOUNT', ".
	     "usr_pswd='$encpass', unix_uid=$uid, notes='$protouser_notes', ".
	     "usr_modified=now(), admin=1, webonly=0, status='active',".
	     "usr_shell='$protouser_shell', usr_email='$protouser_email', ".
	     "mailman_password='$mailman_password',uid_idx=$uid");
196 197
DBQueryFatal("insert into user_stats set ".
	     "  uid='$protouser', uid_idx=$uid, uid_uuid='$uid_uuid'");
198

Timothy Stack's avatar
 
Timothy Stack committed
199
if (!defined($opts{u})) {
200
    $gid_uuid = NewUUID();
Timothy Stack's avatar
 
Timothy Stack committed
201 202 203 204
    
    print "Creating project in database...\n";
    DBQueryFatal("insert into projects set pid='$protoproj', created=now(), " .
		 "name='$protoproj_desc', head_uid='$protouser', ".
205
		 "head_idx=$uid, unix_gid=$gid,approved=1,pid_idx=$pid_idx");
206
    DBQueryFatal("insert into project_stats set ".
207
		 "pid='$protoproj',pid_idx=$pid_idx");
Timothy Stack's avatar
 
Timothy Stack committed
208 209 210

    print "Creating group in database...\n";
    DBQueryFatal("insert into groups set pid='$protoproj', gid='$protoproj', ".
Leigh B. Stoller's avatar
Leigh B. Stoller committed
211
		 "leader='$protouser', leader_idx=$uid, created=now(), ".
Timothy Stack's avatar
 
Timothy Stack committed
212
		 "description='Default Group', " .
213
		 "unix_gid=$gid, gid_idx=$pid_idx, pid_idx=$pid_idx, ".
214
		 "unix_name='$protoproj', gid_uuid='$gid_uuid'");
215
    DBQueryFatal("insert into group_stats set ".
216 217
		 "   pid='$protoproj',gid='$protoproj', ".
		 "   gid_idx=$pid_idx, gid_uuid='$gid_uuid'");
218

219 220
    DBQueryFatal("replace into emulab_indicies set name='next_gid',idx=10000");
}
221

222 223 224 225 226
# Flip UID to the new user so that the mk* scripts are happy - they can't
# be run as root for accountability reasons
$EUID = $UID = $uid;
$EGID = $GID = $gid;

227 228 229 230 231 232 233 234
if (defined($opts{u})) {
    #
    # Creating specific user; it is implied that the user will go into
    # the proto project, which must already exist of course.
    #
    print "Running mkacct...\n";
    if (system "$wap $mkacct -b add $protouser") {
	die "Unable to create account for initial user $protouser\n";
Timothy Stack's avatar
 
Timothy Stack committed
235
    }
236

237 238 239
    print "Running modgroups...\n";
    if (system "$wap $modgrps -a $protoproj:$protoproj:$trust $protouser") {
	die "Unable to add initial user $protouser to project $protoproj\n";
Timothy Stack's avatar
 
Timothy Stack committed
240
    }
Mike Hibler's avatar
Mike Hibler committed
241
}
242 243 244 245 246 247 248 249
else {
    #
    # Creating the initial project. This handles everything.
    #
    print "Running mkproj...\n";
    if (system "$wap $mkproj $protoproj") {
	die "Unable to create initial project $protoproj\n";
    }
Mike Hibler's avatar
Mike Hibler committed
250
}
251

Timothy Stack's avatar
 
Timothy Stack committed
252 253 254 255
if (defined($opts{u})) {
    exit(0);
}

256 257 258 259 260 261 262 263
#
# Okay, if not ELABINELAB, then set the firstinitstate so that the web
# interface will take the user through his first project setup.
#
if (!$ELABINELAB) {
    TBSetSiteVar("general/firstinit/state", "createproject");
}

264
print "User created. Once the web page is up, you should be able to log in\n";
265 266 267
print "as '$protouser' with the password you just entered. Refer to\n";
print "setup-db.txt for instructions on creating a 'real' user account for\n";
print "yourself.\n";