Showing posts with label directory. Show all posts
Showing posts with label directory. Show all posts

Tuesday, May 26, 2009

Controlling Group Access To Directories Recursively And Automatically Using Simple FACL's On Linux

Hey there,

Today we're going to take a look at using simple FACL's (file access control lists) to allow additional access restrictions (for one or more groups other than the directory owner's group) to a directory, while ensuring that the change propagates automatically to all subdirectories and files within all or any. Our examples for today should work on most distro's of Linux (and most proprietary Unix distro's), although the actual commands needed may be different ("man getfacl" or "man setfacl" should sort you out). For today's examples, all commands were run on RedHat Enterprise Server 5.2 (Tikanga - A Mãori word idiomatically meaning "The Mãori Way" or, more sanctimoniously, "correct," "right" or "true" - all absolutes ;)

The situation for today's example is pretty simple, although highly useful (of course, those aren't necessarily opposites ;). You have a directory that is owned by the user "user1" and the group "group1." A user name "user2", in the group "group2" needs to have access to your directory and also needs to be able to add new directories and/or files within that directory. To top it off, when either "user1" or "user2" creates a file or directory within the directory to be shared, that directory and/or file must be accessible to both "user1" and "user2" and owned by the group "group1." Simple enough :)

Of course, the easiest way to do this would be to add "user2" to "group1" and be done with it. However, since that might possibly afford "user2" more privilege than is actually needed (he/she may be able to access other files/directories that are owned by "group1"), a more restrictive solution is required. This is where simple FACL's come into play.

First, we'll take a quick look at the directory in question, with ls -l:

host # ls -ld shared_dir
drwxrwx--- 2 user1 group1 512 May 25 13:24 shared_dir


Once we're done, it will look almost exactly the same, except for the "+" symbol at the end of the alpha permissions display (This is an additional field, as opposed to setuid, setgid and sticky bit which replace the final alpha permissions field with a substitute character) As a point of note; some distributions include the "+" symbol at the end of "every" file, since all files/directories (on a filesystem that supports FACL's) have a default FACL. This seem counter-intuitive to me, since it makes it harder to eyeball which files/directories you've mucked with on purpose ;)

host # ls -ld shared_dir
drwxrwx---+ 2 user1 group1 512 May 25 13:24 shared_dir


Now, in order to make the whole concept of FACL's more accessible, it should be noted (at least, these days) that "all" files have FACL's (see slight caveat above), even if you don't explicitly set them. These FACL's mirror the existing file/directory permissions by default and you'll only the see the "+" symbol on files/directories where the default FACL has been modified (which is what we're going to do).

For instance, the FACL for our original shared_dir, looks like this before we even do anything, and can be obtained with the "getfacl" command (consider owner: user1, group:group1 and permissions of 770 - or drwxrwx---):

host # getfacl shared_dir
# file: shared_dir
# owner: user1
# group: group1

user::rwx
group::rwx
mask::rwx
other::---


All pound (#) comments are not acted upon, just like in most scripting languages. You can remove them, but they'll get automatically recreated every time you run a FACL command.

So, for this directory, we want to allow the group "group2" recursive read, write and execute permissions (read permissions, so they can see the contents of the directory or read any files in it, write permissions, so they can create new files/directories within the directory and/or write to files in it, and execute permissions, so that they can cd into the directory and/or execute files within it)

Luckily for us, this is actually fairly simple. All we need to do is to make use of the "setfacl" command.

host # setfacl -m g:group2:rwx shared_dir


This will change the FACL settings for the shared_dir directory, but won't do "exactly" what we want:

host # getfacl shared_dir
# file: shared_dir
# owner: user1
# group: group1

user::rwx
group::rwx
group:group2:rwx
mask::rwx
other::---


While doing this will allow "group2" read, write and execute access to the directory "shared_dir", the extended access will begin and end with the "shared_dir" directory, and any files/directories created by any user in "group2" will still be owned by "group2" when they are written to this directory.

If we want to satisfy the one remaining requirement (allowing "group2" to create new files/directories within "shared_dir" and have them all belong to "group1", with the unique FACL applied to each and every one), we need to set "default FACL's". Again, this is very simply done, and very easy to manage using the setfacl command Our first command will be to remove the rule we added with our last command. Then we'll set default FACL's for everyone and for the specific users and groups:

host # setfacl -x g:group2:rwx shared_dir
host # setfacl -d -m u:user1:rwx shared_dir
host # setfacl -d -m g:group1:rwx shared_dir
host # setfacl -d -m u:user2:rwx shared_dir
host # setfacl -d -m g:group2:rwx shared_dir


If you use the form above (which is, admittedly, more strict than it needs be; but I'm a control freak ;) and don't see the extra "default" FACL entries for the default user and group that own the file/directory, you can set those like this (also using setfacl):

host # setfacl -m d:u:rwx shared_dir
host # setfacl -m d:g:rwx shared_dir
host # setfacl -m d:o:--- shared_dir


These three entries should be put into your new FACL for the shared_dir by default, when you type the highly-specific FACL commands above, but if they're not, you'll want to add them. They are the key to propagating correct permissions on all new files/directories created within that directory.

Now typing "getfacl" should show you a dramatically different output:

host # getfacl shared_dir
# file: shared_dir
# owner: user1
# group: group1

user::rwx
group::rwx
mask::rwx
other::---
default:user::rwx
default:user:user1:rwx
default:user:user2:rwx
default:group::rwx
default:group:group1:rwx
default:group:group2:rwx
default:mask::rwx
default:other::---


As I mentioned, you could leave out all the commands pertaining to the original owner (and group) of the file, but I like to be as specific as possible, just in case. Now, any file/directory that either user1, user2, or members of group1 or group2 create in this directory will have 770 permissions (rwxrwx---+) and their group will be set to "group1." You can also easily tell that your settings have worked by looking for the "+" symbol at the end of newly created files, which indicates that the FACL has been applied to your new file (although with a slightly different output for files than for directories):

host # who am i
user2
host # touch shared_dir/test
host # ls -l shared_dir
total 0
drwxrwx---+ 2 user2 group1 96 May 1 14:01 test

host # getfacl shared_dir/test
# file: test
# owner: user2
# group: group1

user::rw-
user:user1:rwx
user:user2:rwx
group::rwx
group:group1:rwx
group:group2:rwx
mask::rwx
other::---


And that's that :) If you still don't get the group propagation that you're expecting, it may not be supported on your OS. In that case, you can add this additional command (on top of the FACL's) to ensure that the correct group "sticks":

host # chmod g+s shared_dir


And you should be all set :)

Cheers,

, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, February 25, 2009

Back To Basics: Getting File Information Using Perl's Stat Function

Hey there,

Today we're going take a (somewhat) beginner's look at extracting lots of useful information from files on Linux or Unix using Perl. More specifically, we'll be using Perl's stat() function. If you're a regular reader, you've probably noticed that we've strayed quite a bit from our original motto (which is screaming in the meta-description tag of every page ;) which is to keep up a blog that appeals to Linux and Unix users and/or admins of any skill level. Lately, we kind of feel like we've neglected the folks newer to the Linux/Unix world. Perhaps we're wrong. We're obviously confused. Perhaps, the next time we start a blog we'll be bit more specific about the subject matter. That being said, we've made our beds and have resigned to lie in them, since the floor is getting uncomfortable again ;)

Today we're going to take a brief introductory look at Perl's stat() function and how you can use it to extract lots of useful information from files, directories, etc (actually, everything in Linux/Unix is a "file" of a certain type). It's incredibly easy to use and, even if you've never used Perl before, very easy to implement. If you're still stuck at the Shebang line in point of reference to Perl/shell scripting, check out that post, as we hope it provides a good base for one of the things you're most likely to find in a Perl or shell script. It also goes beyond just scripting and attacks some other basic concepts.

Here's a very simple Perl script (which, as demonstrated, can be written on the command line as well) that will let you know the user id and group id associated with a particular file. We won't be exploring stat() completely in this post. just enough to get you started and comfortable (you could get the same information by running "ls -l" instead of just "ls," but that would defeat the purpose, right? ;):

host # ls
file.txt
host # perl -e '$file="file.txt";($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat($file);print "$file : User: $uid - Group: $gid\n"';
file.txt : User: 1000 - Group: 513


Your output may vary depending on what flavour of Linux or Unix you're using. In script format, this command line would look this way (not really all the much different, but, perhaps, easier to read):

#!/usr/bin/perl

$file="file.txt";
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat($file);
print "$file : User: $uid - Group: $gid\n"';


When you run this (after giving it execute permission for the user - you :) - ) you get the same result (assuming you called it file.pl):

host# ./file.pl
file.txt : User: 1000 - Group: 513


Granted, today's examples were rudimentary for anyone who's had experience with Perl's functionality and the Unix/Linux shell, but, in this on and off series, we're going to take it a bit slow (we'll probably build on this post every two or three other posts). This is in no way an attempt to dumb-down the blog, just to get back to basics. Think of it as a bracing slap in the face we're giving ourselves to remind us that this blog isn't all about higher concepts.

All of us here, at one point, didn't know what Linux or Unix was, much less how to make any use of it. This interweaving series is for those of you who are just getting started (just like everyone in the business did at some point :)

We look forward to getting more into it. Perhaps the learning curve will be surprisingly fast. At the rate our children are picking up on computers, perhaps the transfer of knowledge won't be quite as difficult as it was in our day. Who can say? We don't know, but we're positive that, as soon as they can, they will ;)

In our next post in this series, we'll look at the components of the Perl stat() function and the rest of the information you can gather from it. Hopefully this post has been easily digestible enough to act as an introduction. If not, please contact us (via the link at the right hand top of every page) and let us know. We write a lot for ourselves (especially Mike, who thinks everything that makes him laugh is funny to everyone else in the entire universe ;), but are committed to providing useful information for you :)

Cheers,

, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, February 4, 2009

The Linux/Unix SysAdmin Covert File Storage Method Number 57

Hey there,

Today's post is the 57th installment of a two or three part series of posts that refuses to play by the rules. Look for Volume 3 and Issue 437 in the near future ;)

This post's trick (actually it's more of a gimmick - or a way any one of us has probably screwed up at some point in time ;) is fairly simple and, as is generally the case, inversely proportionate in complexity to the work I'm currently gettting paid to do so my wife, kids and 5 animals don't go hungry. I'm somewhat obsessive-compulsive and tend to forget to eat more often than I remember to. Metabolism, of course, is just another one of life's cruel jokes. I'm not gigantic, but my waistline implies a lavish and sedentary lifestyle I don't enjoy. Actually, my fitness-oriented friends tell me that I'd lose the spare-tire if I just ate more regularly. While this makes perfect sense, I generally don't ;) ...and then, every once in a while, I digress...

This little goof is pretty simple to pull off (assuming you're a sysadmin and/or have the access, privilege and opportunity to do it) and can be a life-saver. Technically, you shouldn't ever need to do this, but sometimes convenience trumps sanity...

You may recall a post we did a long long time ago, in a galaxy just down the block, regarding finding space hogs on multiple overlay-mounted filesystems. This little way to hide bits of information works relatively along the same lines. The one serious limitation it has is that, while you'll be secretly storing your information, you won't be hiding the actual disk space it consumes, so this method of packing away all the stuff you're not supposed to have on the company's production web server has its limitations.

For today, we'll use a /usr/local mount point that we have on a Solaris machine (independent of the /usr mount point) to demonstrate.

Step 1: Take a lay of the land. In order for this to work, you need to have enough space to stow away what you need to and, hopefully, enough space to make your addition barely noticeable. Our setup isn't bad, especially since the "actual" filesystem that's going to be impacted will be the /usr filesystem underneath /usr/local (If it were /usr/local, the change might be noticed since the filesystem is so "empty")

host # ls /usr/local
PKG-Get etc lib lost+found pkgs share
bin info libexec man sbin
host # df -k /usr/local
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s5 51650439 167184 50966751 1% /usr/local
host # df -k /usr
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s3 5233086 3550979 1629777 69% /usr


Step 2: Peel back the carpet. This is where you have to be quick, and is the essence of our little shell-game. First, we'll unmount the /usr/local filesystem, leaving us with this (df -k for /usr/local now shows that it's just a simple directory on /usr):

host # umount /usr/local
host # df -k /usr/local
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s3 5233086 3550979 1629777 69% /usr
host # ls /usr/local


Under normal circumstances, this directory should now be empty (unless someone else is doing the same thing as you, or just forgot to clean up before they created the separate /usr/local overlay mount).

Step 3: Sweep your non-work-related-stuff under the rug, or into the /usr/local directory, as it were:

host # mv non-work-related-stuff /usr/local/
host # ls /usr/local
non-work-related-stuff


Step 4: Make sure things look normal. See if your addition makes a noticeable difference in your df output (It probably won't unless you're going to try and sack-away your mp3 collection ;)

host # df -k /usr
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s3 5233086 3550981 1629775 69% /usr


Step 5: Pretend nothing happened. Once you're satisfied (which should be as soon as possible), remount /usr/local and verify that everything looks the same (excepting your modification of the /usr filesystem):

host # mount /usr/local
host # ls /usr/local
PKG-Get etc lib lost+found pkgs share
bin info libexec man sbin
host # df -k /usr/local
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s5 51650439 167184 50966751 1% /usr/local
host # df -k /usr
Filesystem kbytes used avail capacity Mounted on
/dev/dsk/c0t0d0s3 5233086 3550981 1629775 69% /usr


And you're all set. You can get your stuff back eventually (even sooner if you don't care what people think ;)

Of course, that's an old trick, but one we've never covered here. As this blog gets larger, we're going to try and devote a little less time to being original. Of course, we mean that in a good way :) Since this is essentially a knowledge-dumping-ground, meant for users and admins of all skill levels, every post can't be about some crazy way to do something you'd have to be insane to want to do in the first place!

Come on in; the mediocrity's fine ;)

Cheers,

, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, March 17, 2008

Perl Directory Permissions Difference Script For Directories

Howdy,

Today's Perl script is a slight variation on the "diff -r" command (actually the diff command with -r option) for both Linux and Unix. "diff -r" recursively checks directories to report on the differences, much in the same way diff (without arguments) might compare two files. This is a great command if you need to manage separate directory trunks or the find the difference in them. For an example, consider two directories we've created, each with a few files in it, and the output that "diff -r" produces:

host # diff -r scripts1 scripts2
host #


No output means both directories are identical.

There do exist situations, however, for which "diff -r" falls a bit short. The pdiff Perl script presented today works more like "diff -rs" (the -s option let's you view two files, or directories, side by side). Using our previous example, we'd get this output instead:

host # diff -rs scripts1 scripts2
Files scripts1/script1 and scripts2/script1 are identical
Files scripts1/script1.bak and scripts2/script1.bak are identical
Files scripts1/script2 and scripts2/script2 are identical
Files scripts1/script2.bak and scripts2/script2.bak are identical
Files scripts1/script3 and scripts2/script3 are identical
Files scripts1/script3.bak and scripts2/script3.bak are identical


A little bit better, since it will at least tell you that the directories are identical ;)

However, diff doesn't take directory permissions, or user/group ownership, into consideration, which is why I wrote this script. If we run pdiff against those same 2 directories, we get this output:

./pdiff scripts1 scripts2
0644 script1---eggi newpeople 0755 script1---eggi newpeople
0644 script1.bak---eggi newpeople 0755 script1.bak---eggi newpeople
0644 script2---eggi newpeople 0755 script2---eggi newpeople
0644 script2.bak---eggi newpeople 0755 script2.bak---eggi newpeople
0644 script3---eggi newpeople 0755 script3---eggi newpeople
0644 script3.bak---eggi newpeople 0755 script3.bak---eggi newpeople


Notice the difference? ;)

Hope you find good use for this script, and enjoy!

Cheers,


Creative Commons License


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

#!/usr/bin/perl

#
# pdiff - diff directories with
# user/group and permissions checked
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ($#ARGV != 1) {
print "Usage: $0 dir1 dir2\n";
exit;
}

$firstdir=$ARGV[0];
$seconddir=$ARGV[1];

@firstfiles=`ls -l $firstdir|grep -v total`;
@secondfiles=`ls -l $seconddir|grep -v total`;
@file1 = ();
@file2 = ();

foreach $firstbit (@firstfiles) {
@firstparts = split(/ * */, $firstbit);
@perms = split(//, $firstparts[0]);
chomp(@perms);
@user = ($perms[1], $perms[2], $perms[3]);
@group = ($perms[4], $perms[5], $perms[6]);
@other = ($perms[7], $perms[8], $perms[9]);
$ucounter = 0;
$gcounter = 0;
$ocounter = 0;
$majordiff = 0;
foreach $perm (@user) {
if ($perm eq "r") {
$ucounter += 4;
} elsif ($perm eq "w") {
$ucounter += 2;
} elsif ($perm eq "x") {
$ucounter += 1;
} elsif ($perm eq "s") {
$ucounter += 1;
$majordiff += 4;
}
}
foreach $perm (@group) {
if ($perm eq "r") {
$gcounter += 4;
} elsif ($perm eq "w") {
$gcounter += 2;
} elsif ($perm eq "x") {
$gcounter += 1;
} elsif ($perm eq "s") {
$gcounter += 1;
$majordiff += 2;
}
}
foreach $perm (@other) {
if ($perm eq "r") {
$ocounter += 4;
} elsif ($perm eq "w") {
$ocounter += 2;
} elsif ($perm eq "x") {
$ocounter += 1;
} elsif ($perm eq "t") {
$ocounter += 1;
$majordiff += 1;
}
}
@permissions = ($majordiff, $ucounter, $gcounter, $ocounter);
$permissions = join("", @permissions);
# print "$permissions $firstparts[8]";
chomp($firstparts[8]);
push(@file1, "$permissions $firstparts[8]---$firstparts[2] $firstparts[3]");
}
foreach $firstbit (@secondfiles) {
@firstparts = split(/ * */, $firstbit);
@perms = split(//, $firstparts[0]);
chomp(@perms);
@user = ($perms[1], $perms[2], $perms[3]);
@group = ($perms[4], $perms[5], $perms[6]);
@other = ($perms[7], $perms[8], $perms[9]);
$ucounter = 0;
$gcounter = 0;
$ocounter = 0;
$majordiff = 0;
foreach $perm (@user) {
if ($perm eq "r") {
$ucounter += 4;
} elsif ($perm eq "w") {
$ucounter += 2;
} elsif ($perm eq "x") {
$ucounter += 1;
} elsif ($perm eq "s") {
$ucounter += 1;
$majordiff += 4;
}
}
foreach $perm (@group) {
if ($perm eq "r") {
$gcounter += 4;
} elsif ($perm eq "w") {
$gcounter += 2;
} elsif ($perm eq "x") {
$gcounter += 1;
} elsif ($perm eq "s") {
$gcounter += 1;
$majordiff += 2;
}
}
foreach $perm (@other) {
if ($perm eq "r") {
$ocounter += 4;
} elsif ($perm eq "w") {
$ocounter += 2;
} elsif ($perm eq "x") {
$ocounter += 1;
} elsif ($perm eq "t") {
$ocounter += 1;
$majordiff += 1;
}
}
@permissions = ($majordiff, $ucounter, $gcounter, $ocounter);
$permissions = join("", @permissions);
# print "$permissions $firstparts[8]";
chomp($firstparts[8]);
push(@file2, "$permissions $firstparts[8]---$firstparts[2] $firstparts[3]");
}

$file1count = @file1;
$file2count = @file2;

if ( $file1count > $file2count ) {
$filecount = $file1count;
} else {
$filecount = $file2count;
}

$indexer = 0;
while ( $indexer < $filecount ) {
chomp($file1[$indexer]);
chomp($file2[$indexer]);
printf("%-40s%s\n", $file1[$indexer],$file2[$indexer]);
$indexer++;
}


, Mike