Showing posts with label user. Show all posts
Showing posts with label user. Show all posts

Saturday, June 21, 2008

Security Basics For Linux and Unix Installations - Part 2

Hey there,

Following up on our first two posts on basic Linux and Unix installation security and even more installation security measures, we're going to move on to the next section. Today, we'll be focusing more specifically on user, and group, accounts, and modifications that we want to make in that arena.

Again, this series of posts is based mostly on Solaris Unix and RedHat Linux, so it can't possibly apply to everything. I welcome any comments regarding it, especially as they pertain to additional security and other Operating Systems. Just to be clear, also, we won't be touching on more advanced stuff like setting up firewalls, etc. The summary of this 5-part post should end up being a bullet list that you can run through and take care of without having to go too crazy ;)

And, of course, my suggestion, to everyone who has the rights and means, is to script out as much of this method as possible. However you end up securifying your system, once you have your routine down, having Jumpstart or Kickstart do it for you from then on out is much less of a hassle (usually :)

See you tomorrow for section 3!

Cheers,

OS Installation Guidelines, Part 2: Users and Accounts

1. Add accounts for all necessary users. Require valid reasons for all accounts created.

2. You can ignore this step if you're using Solaris or RedHat's OpenSSH pkg or rpm, since it will be complete already: Be sure to add a user account for sshd. This will be required for our OpenSSH to run correctly. Should be as simple as

host # useradd –m –d /users/sshd sshd
host # passwd –l sshd
<--- (The passwd –l just locks the password so no one can login to the account. This is okay, since the sshd account is only necessary for privilege separation.

IMPORTANT NOTE FOR POINTS 3 THROUGH 6 REGARDING USER REMOVAL: Please note that for point three, we specify "userdel -r," because these users have home directories that are NOT "/" - If there is any doubt in your mind that an account you want to remove has the root filesystem as its home directory, use "userdel" without the -r, as use of that flag may effectively destroy your system. An easy way to find out what a user's home directory is involves simply grepping the username out of /etc/passwd and checking out the second to last field.

3. userdel –r lp, uucp, nuucp, some, all or more if possible (the nobody account should be removed, too, unless it’s used by a web server or some other application). Note that your list will be larger on RedHat since they generally have a user for every service and you should remove every user for every service you're not using if it wasn't removed by rpm -e. Also, on Redhat, be sure to check for matching entries for the user in /etc/group

4. userdel nobody (This should not be removed if it’s used by a web server or some other application)

5. userdel smtp (Non-existent in Solaris9).

6. userdel mail (Non-existent in Solaris9).

7. chmod 700 all user directories.

8. Comment out gsscred and nfsfind from root cron on Solaris. Delete sys, lp, adm, root.au, uucp and all unnecessary crontabs, if possible.

9. Add “0,5,10,15,20,25,30,35,40,45,50,55 * * * * /usr/lib/sa/sa1” and “59 23 * * * /usr/lib/sa/sa2 -b -g -r –u” to root's cron. On RedHat you'll need to have the sysstat RPM installed in order to use sar. If it "is" installed on Redhat, check out /etc/cron.d to make sure that it is being run in cron and modify the setup per above, to increase or decrease the number of times it runs, if you need to.

10. Create the directory /var/adm/sa (/var/log/sa on RedHat) if it doesn't exist, and chown it to root:yourAdministratorsGroup. Then chmod the directory to 660.

11. Add “/usr/lib/acct/ckpacct” to root's cron on Solaris, if the machine does not have high user activity and has adequate disk space for logging. Remove any other /usr/lib/acct lines in the Solaris root crontab. For RedHat users, make sure that you have the psacct RPM installed. The default settings should be okay and you can use chkconfig or ntsysv to ensure that process accounting is set to run at boot time.

12. Remove user directories for users who no longer have accounts on the machine.

13. Remove crontab entries in /usr/spool/cron/crontabs (Solaris) or /var/spool/cron (RedHat) for users that no longer have accounts on the machine.

14. Add empty .rhosts, .netrc and .exrc files in all user home directories.

15. chmod 0 and chown root all user .rhosts, .netrc and .exrc files.

16. userdel (–r, if their home directory isn’t "/" ) all users, aside from root, with uid 0.

17. Change the group for any user (other than root) that has a gid of 0, if possible.

18. Be sure to include a umask of 022 in any user’s .profile if their account will require their home directory to be somewhat publicly available. Examples of such an accounts include development or group-based accounts. These kind of accounts are used to ease the ability of a group to work together. Of course, the standard 027 umask would make this impossible. Please use discretion and do this only for accounts where the need is real.

19. For all generic accounts, make them su-only. You can find detailed instructions on how to do this, including some pitfalls and stumbling blocks you may encounter, in our post on making generic accounts su only. As luck would have it, it's still aptly named ;)

to be continued...

, Mike

Thursday, January 3, 2008

Bypassing Boot Server Installation When Jumpstarting Across Subnets

Greetings,

Today I thought we'd go with a little "trickery," although this isn't really a trick; it's just not the way you're supposed to use Solaris' Jumpstart when Jumpstarting across subnets. It has it's benefits, though, aside from the obvious (I'll get to that at the end :) Note that some of this process varies in Solaris 10 (insofar as the starting up of services goes, but we'll hit that in a later post, since they've completely changed the procedure). This will work, almost word for word in Solaris versions up to and including 9.

Now, to get started:

In order to install and configure a system across subnets, it is recommended by Sun that the Jumpstart client be configured to run through a Boot Server on that client's subnet (and on a separate server for every subnet that you need to hop through). This involves executing the add_install_client command on the Boot Server on the same subnet as the client, and installing all the software required to run a Boot Server.

It is possible, however, to use any server on the client's subnet as a conduit through which to pass the boot, installation and configuration information between the client and server without expending the time, or wasting the resources, that installing a Boot Server on the intermediate client requires.

In order to make this seamless connection between subnets possible, and allow your Jumpstart client to install and configure itself through the server, you will need to modify certain files and ensure that several daemon processes are active on the gateway computer.

1. First, be sure to update the gateway computer's /etc/hosts and /etc/ethers files to include entries for the install client. The gateway computer is were we'd normally have to consume the disk space to actually set up a legitimate Boot Server.

2. Ensure that the proper daemons are active on the gateway computer. You may need to start up rarpd, bootparamd and tftpd (which is, by default, commented out of the /etc/inetd.conf file, so you'll need to "kill -1" or "kill -HUP" the inetd process after uncommenting that entry).

3. Create an entry for the install client in the /etc/bootparams file. You can generally copy the entry that exists in your Jumpstart server's /etc/bootparams file, but you will need to make a few changes:

a. Translate the hostname of the server (if entered in this fashion) to it's I.P. Address.

b. Adding an ns entry won't hurt. Assuming a Jumpstart server with I.P. Address 192.168.0.12, no supported nameservice active and a netmask of 255.255.255.0, you can add this to the end of the /etc/bootparams entry for your client (on the gateway computer, of course):

Ex:

ns=192.168.0.12:none(255.255.255.0) <-- Add a colon to the beginning of this addition if the /etc/bootparams entry doesn't end with one!


4. Create a directory off of the root partition named tftpboot.

Ex:

mkdir /tftpboot


5. Populate your /tftpboot directory with the contents of the /tftpboot directory on your Jumpstart server host and ensure all file and directory ownership and permissions are the same. If you know exactly what to look for, you really only need to copy the files that are relevant to the server you're trying to Jumpstart, but you can't go wrong by copying them all.

Now, the intermediate host/server should be prepared to accept your Jumpstart client's rarp requests and transparently forward them to your Jumpstart server on the alternate subnet!

Note that you can do this on as many servers as you like (if, for instance, you have to take multiple hops across subnets to get to your Jumpstart server). Just duplicate on each intermediate host. The only thing you'll have to change (and sometimes you won't even have to do this) is the Jumpstart host listed in /etc/bootparams on the servers that come before you hit the server that can get to the Jumpstart server. You can create a simple daisy chain by replacing the Jumpstart server name with the IP of the next host along the way in the /etc/bootparams file. Hopefully, you won't ever have to hop more than once to get to an internal network that has a Jumpstart server on it.

And finally, as promised, the extra benefit: If you ever get stuck at home and have to boot a Solaris box that won't come up, the box is on a subnet without a Jumpstart (or Boot) server and there's no one at the office to put in a CD for you to boot off of, you can use this exact same procedure to hop subnets and get to single user mode with:

boot net -s

Cheers,

, Mike




Monday, December 3, 2007

Finding User Accounts To Disable - Follow-Up Post

Hey there,

This is a follow up to a previous post where we're walking through the process of developing a decent script, from scratch; examining how to figure out what user accounts you could disable or delete (times vary, depending on your company's policy) on any particular box you have to administrate.

Today I've taken it a step farther. At this point the below script will check the modification time of all users listed in /etc/passwd and compare it against the current Unix time. If the mtime on the home directory is over 45 days and less than 90, we warn to disable (actual code not included) and if it's over 90 days, we warn to delete (same "no code" policy for that ;). If the time on the home directory is under 45 days, the account is consider active and OK.

Note that I found a few incorrect things and changed them, as I augmented this script; so this isn't just a bigger version of the original. First of all, it seems that the user's home directory is just as reliable as the existence of a history file. Also, it's much more likely to exist ;) Also, by verifying, I noticed that the mtime (Last Modification Time) was more reliable than the atime (Last Access Time) that we used before. That seems a bit counter-intuitive, but so far, it's proving out.

We've also added some extra functionality to remove users that we don't want to check. The first group are in a file we've called locked.txt, the second group are users without a valid shell and the final group are users who are in a particular group (like administrators who need accounts on all boxes, but might not login to any particular one for a long time).

Hope this script helps, at least, to point a few people in the right direction. This is a work in progress since it still contains several flaws, including no cross-referencing of possibly-available data that the system can provide with "last" and other such commands, as well as still having to deal with "touch."

Note that, on the lines ---> @locked_text = ###LOCKEDTEXT###;
and
open(LOCKEDTEXT, "###locked.txt");

the ### space-holders should be greater than and equal signs <>, in the first instance, and a < sign in the second, but the blog software interprets them as a unclosed and unallowed tags!

Have fun :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# switching to mtime 9 - from atime - 8
#
# We don't need to check for errors on the command line since
# we won't be accepting any arguments
#

if ( -f "moreinfo") {
print "Removing old temporary file \"moreinfo\"\n";
unlink("moreinfo");
}

$today=time();

@no_shells=`egrep -i 'nologin|false' /etc/passwd|awk -F":" '{print \$1}`;
@in_unixtech=`grep 1621 /etc/passwd|awk -F":" '{print \$1}`;
@user_names=`awk -F":" '{print \$1}' /etc/passwd`;
open(LOCKEDTEXT, "###locked.txt");

@locked_text = ###LOCKEDTEXT###;
close(LOCKEDTEST);
print "\nRemoving LOCKED ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@locked_text ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving NO_SHELL ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@no_shells ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving UNIXTECH ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@in_unixtech ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n\n";
@total = @both = @unique_names = ();
%count = ();
foreach $possible_dupe (@user_names, @tmp_user_names) {
$count{$possible_dupe}++
}
foreach $possible_dupe (keys %count) {
push @total, $possible_dupe;
push @{ $count{$possible_dupe} > 1 ? \@both : \@unique_names }, $possible_dupe;
}
@user_names = @unique_names;
foreach $diruser (@user_names) {
$filer = `grep $diruser /etc/passwd|awk -F":" '{print \$6}'`;
push(@user_dirs,$filer);
}
print "FIRST RUN - Checking Home Directories...\n\n";
foreach $history_file (@user_dirs) {
chomp($history_file);
chomp($user_name = $user_names[$counter]);
print "${user_name}: ";
$counter++;
if ( -d "$history_file" ) {
@file_dates=stat("$history_file");
$file_date=$file_dates[9];
} else {
$file_date=0;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
print "User needs to be disabled - ";
print "45 days old or older, but under 90\n";
# YOUR CUSTOM CODE HERE - "passwd -l" would probably suffice"
} elsif ( $difference >= 7776000 ) {
print "User needs to be deleted - ";
print "90 days old or older\n";
# YOUR CUSTOM CODE HERE -
} else {
print "OK\n";
}
}


, Mike





Saturday, December 1, 2007

Disabling and Removing Old User Accounts

Depending upon the size of your network, keeping track of user accounts on all of your Unix and Linux boxes can range from fairly simple to unbelievably hard to keep track of. I've noticed on the internet boards that lots of people are looking for the "one" way to semi-automate determining if user accounts need to be disabled, or if they're so old they need to be deleted.

I'm not a believer in the "one" solution theory. One of the things I love about Linux and Unix is, depending on how creative you're willing to be, there are probably 50 or more ways to do anything.

In any event I started looking at this problem, and noticed that almost everyone was using the "finger," "logins" or "last" commands to figure out how long it had been since someone logged in. In some extreme cases, I've even found examples where folks "pack" and "unpack" /var/adm/wtmpx with Perl to get the information. So, I naturally thought: Is it possible to do this without using the system's login-logging facilities?

The more I thought about this, the more I came to understand that both methods had their virtues and their faults. For instance, if /var/adm/wtmpx is missing, a lot of regular system accounting programs won't work. If you corrupt that file and remove /var/adm/lastlog (I'm using Solaris as my model - substitute file locations and names as necessary), "logins," "finger" and "last" don't report correctly unless they're reporting an error. The alternative approach also has its flaws. If, for instance, a user utilizes SSH to run a single command from a remote host using key-based passworless login, that shell won't be interactive and it won't update any shell history files!

Tackling this problem will take more than one post, so we'll start out with the core; treat this as a sort of tutorial on the phases that you go through from scratch to a decent script. Our assumption here will be that we have no access to any of the regular utilities like "last," "finger" and "logins." We'll use Perl to check out all users' .sh_history or .bash_history files (just covering sh, ksh and bash for now) and use the "stat" and "time" functions to determine how long it's been since the user last logged in.

Here's the core of the code, to get started. I've written it so it can be run on its own for now:


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
# We don't need to check for errors on the command line since
# we won't be accepting any arguments
#

$today=time();

@user_dirs=`awk -F":" '{print \$6}' /etc/passwd`;
foreach $history_file (@user_dirs) {
chomp($history_file);
print "${history_file}: ";
if ( -f "$history_file/.sh_history" ) {
@file_dates=stat("$history_file/.sh_history");
$sh_file_date=$file_dates[8];
} else {
$sh_file_date=0;
}
if ( -f "$history_file/.bash_history" ) {
@file_dates=stat("$history_file/.sh_history");
$bash_file_date=$file_dates[8];
} else {
$bash_file_date=0;
}
if ( $sh_file_date > $bash_file_date ) {
$file_date = $sh_file_date;
} elsif ( $bash_file_date > $sh_file_date ) {
$file_date = $bash_file_date;
} else {
print "Cannot find .bash_history or .sh_history for $history_file. Moving on!\n";
next;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
print "User needs to be disabled- ";
print "45 days old or older, but under 90\n";
# YOUR CUSTOM CODE HERE - "passwd -l" would probably suffice"
} elsif ( $difference >= 7776000 ) {
print "User needs to be deleted - ";
print "90 days old or older\n";
# YOUR CUSTOM CODE HERE -
} else {
print "OK\n";
}
}


Within the next post or two, we'll follow up on this, so that we can add functionality to check system files, using system utilities, to verify that our initial information is correct. It's possible that someone just "touch"ed a user's history file and that access time information isn't necessarily correct. Also, as we noted above, folks can be using the host remotely, in a non-interactive shell, and never alter the access time of their history file.

For the time being, this code should be a good start, and something to think about and improve upon. Until next time :)

, Mike