Skip to content

Simple, reliable, rate-limited backups

Wow, once again it’s been a long time since I’ve written anything. The series on marrying Active Directory with *nix systems is by no means dead, I’ve just been forced to spend all of my research time tinkering with aspects of AD integration that don’t really fit in well with the series, so until I get past that, it’ll remain on hold.

In the meantime, I’ve been meaning to write up a brief post about a backup script that I’ve been using to back up a number of servers that I manage. Briefly, the script archives a local copy of the previous backup, creates tarballs of specific directories for the current backup, then transfers those to a remote backup server. That gives me at least one good local backup at any given time (two when the script is not actually running) and as many remote backups as I choose to keep — I’ve long considered implementing a `find` cron job to clean up old backups, but disk space is cheap, so I usually let them sit as long as possible and manually clean them up a few times a year.

The script doesn’t rely on much that you wouldn’t expect to have on a typical *nix server — tar, gz, scp are the minimum requirements, and screen is highly recommended if you choose to run the script manually. So, without further ado… actually, a disclaimer is in order — the fundamental concepts of this script I came across while lending a hand to a friend of mine who needed someone to keep an eye on his server while away on an extended trip around the world. I’ve polished it up, added some sanity checks, and combined a two step process into a single script, but the script below is definitely a derivative work, so credit is due to Trevor for his original work.

backupscript.txt

So, let’s break it down:

#!/bin/bash

Bash is definitely a requirement, as we’ll use it’s string manipulation abilities to munge multilevel paths into sane filenames.

backup_dirs="/etc/backup_dirs"
backup_path="/export/backup"
backup_location="backup@myserver.com:/export/backup/myfiles/"
scp_opts="-l 4096"

The variables set here are:

  • $backup_dirs is a text file containing a list of directories to back up, one per line, and I’m pretty sure trailing slashes are discouraged, though adding a line to make them merely irrelevant would be trivial.
  • $backup_path is the directory in which the local backups will be kept. It shouldn’t initially contain anything other than a directory called old.
  • $backup_location is the remote path that the backups will be transferred to via scp. This definitely works best if you have SSH keys set up
  • $scp_opts is a list of additional options that can be passed to scp for the remote transfer — in this example, it’s used to rate limit the connection to 4Mbps
# check for screen
if [[ $TERM != 'screen' ]] then
  echo "ERROR: seriously, run me inside screen"
  exit 1
fi

This is a completely optional check to make sure the script isn’t being run from a terminal session that you might not want to rely on keeping open — starting a screen session first will spare you some headaches if you’re running the script manually. If you plan to use the script both from cron and for manual backups, prepending `TERM=screen` to the cron job will allow you to keep the check in place but still easily trigger backups via cron.

# make sure we have list of directories to backup
if [[ ! -f $backup_dirs ]] then
  echo "ERROR: $backup_dirs not found; nothing to backup!"
  exit 1
else
  for dir in `cat $backup_dirs` ;  do
    if [[ ! -d $dir ]] then
      echo "ERROR: $dir is not a valid directory!"
      exit 1
    fi
  done
fi

This goes through the file $backup_dirs and makes sure that the listed items are in fact directories that exist on the filesystem. If either the file or any of its entries are not found, the backup will not continue.

# get passphraseeval `ssh-agent -s`ssh-add

Another optional element — I highly recommend passphrases on keys, and if you don’t use a utility such as keychain to manage your passphrases, this snippet will allow you to enter the passphrase at the beginning of the script, so that it will be stored when the script is ready to start the uploads.

# run local backup
echo backup started: `date`now=`date +%Y%m%d`
nice rm $backup_path/old/*.gz
nice mv $backup_path/*.gz $backup_path/old
for dir in `cat $backup_dirs` ; do
  echo $dir  dirname=${dir:1}
  nice tar -zcf $backup_path/${dirname//\//_}-$now.tar.gz $dir
  chmod 0600 $backup_path/${dirname//\//_}-$now.tar.gz
done
ls -l $backup_path/*.tar.gz
echo backup complete: `date`

Here we get the current date, saved as a variable to keep the backup set together in case the tar processing time overlaps multiple days. If you expect to take multiple backups in a single day (I choose to do weekly backups, but I’ve set this script up for daily/monthly backups as well at times) you’ll want to tweak the `date` command to include a more detailed timestamp. Next we remove the oldest backup from $backup_path/old, move the most recent backup into $backup_path/old, and start a new backup. The manipulations of $dir -> $dirname are intended to strip away the leading slash and to replace any internal slashes with underscores, so that a backup for /var/www (on 31-Oct-09) would be saved as var_www.tgz-20091031.

# upload
echo upload started: `date`
scp $scp_opts $backup_path/*.tar.gz $backup_location
echo upload complete: `date`

Lastly, we start the upload. This is almost anti-climactic. If you get charged for straight bandwidth usage (or don’t have to pay for bandwidth at all) then rate-limiting via the $scp_opts variable is a concern only if you need to reserve bandwidth or processing time (encryption can be expensive at higher speeds) for other tasks. But if you pay for a burstable connection, you’ll want to make sure that the rate limiting options are tuned to keep the bandwidth usage (of the backup plus any baseline/expected traffic) below your trigger rate.

In conclusion — this is definitely not the only way to do backups. It’s certainly not the most network or space efficient. It is, however, quite reliable and simple enough to be easily grasped, an important factor if you’re concerned about making sure that you actually know how to recover from your backups — and you definitely should be.

Categories: Random.

Tags: , , , , ,

My First PowerShell Script: Duplicating NTFS ACLs between file trees

I wrote my first PowerShell script this morning; it wasn’t something I’d planned to do today — or for that matter, at any point in the near future — because while I have nothing against PowerShell, I really haven’t had enough interest in it to make spending time with it a priority.

Over the weekend, though, I migrated a couple of CIFS shares from one server to another, and while I’d copied ACLs on all of the files and subdirectories, the permissions on the top-level folders housing each shares weren’t set correctly. Once users came in this morning, they were able to access their existing files, but not browse the shares. One of my coworkers beat me in to work today, and due to a difference in the default behavior of Vista and older versions of Windows, he managed to clobber all of the permissions on the file shares, replacing them with a single set of ACLs for all files in each share.
Once I arrived, we kicked back and forth a couple of ideas on how best to fix the problem without disrupting the users’ work, and finally decided that my best bet was to write a script to traverse the original filesystems, check the ACLs on each file/directory, and apply those to any existing files in the new filesystems that had the same name. I was vaguely familiar with using perl and setacl.exe to do this type of thing, and wasn’t looking forward to the headache, so we started looking at alternatives, and PowerShell came up as an option; its Get-Acl and Set-Acl cmdlets (offtopic: who the hell thought “cmdlet” was a good name!?) are naturally suited to duplicating ACLs. The only problem? My aforementioned unfamiliarity with PowerShell.
Actually, it turns out that I had another problem — PowerShell wasn’t even installed on the machine that I was using to connect to all of the relevant shares. After installing it — along with its requisite specific version of .Net Framework — I was ready to go… and had no idea how to start.
I won’t bore you with the details of how I figured out all the little bits of syntax that went into making my script, other than to say that Google yet again saved my bacon. I wasn’t able to find a script that did exactly what I wanted, but since walking though file trees and setting ACLs are both fairly common tasks, it wasn’t too hard to come up with this first version of the ‘tree-copy-acls.ps1′ script:
# get paths
$src = $args[0]
$dest = $args[1]

# walk the source file tree and copy acls to files in dest
Dir $src -recurse | ForEach-Object {
$path = $_.FullName.Replace($src, $dest)
get-acl $_.FullName | set-acl -path $path
}
invoking this as “& .\tree-copy-acls.ps1 <SOURCE> <DEST>” spewed out errors on nearly every file — changing the ownership of the files is part of the set-acl process, and by default your choices for the new owner are limited to the user account making the change and the “Administrators” group. Processes running with the “SeRestorePrivilege” security token, however, have no such limitation. Looking for a way to get myself of one of these tokens, I headed back to Google, and discovered the “PowerShell Community Extensions” or PSCX, a set of PowerShell helpers that allows (among many, many other things) a script — running with appropriate access, of course — to manipulate security tokens. Evidently, this specific task is one that PSCX gets used for quite a bit, because their website states that they’re planning to add a one-liner that allows access to modify ownership; the process for the current version, however, isn’t too bad:
$SeRestorePrivilege = New-Object -typename \
PSCX.Interop.TokenPrivilege -argumentlist \
“SeRestorePrivilege”, $true
Set-Privilege $SeRestorePrivilege
Note: I have no idea whether the “\” convention I’ve used above to note line continuation works in PowerShell — so unless you’re more experienced with PowerShell than I and know the answer, just make sure that the first three lines of the above snippet are actually a single line, with no “\” characters in it.

Installing PSCX and adding this into the script made everything work, so ‘tree-copy-acls.ps1′ version 0.2 looks like this:
# get paths
$src = $args[0]
$dest = $args[1]

# allow script to set ownership
$SeRestorePrivilege = New-Object -typename \ PSCX.Interop.TokenPrivilege -argumentlist \
“SeRestorePrivilege”, $true
Set-Privilege $SeRestorePrivilege

# walk the source file tree and copy acls to files in dest
Dir $src -recurse | ForEach-Object {
$path = $_.FullName.Replace($src, $dest)
get-acl $_.FullName | set-acl -path $path
}
Obviously, there are a few tweaks that could make the script cleaner — checking for proper input, printing a usage message in case of invalid arguments, and sanity checking each file during the ForEach loop to avoid trying to set ACLs on nonexistent files in the destination tree would make the script a little more user friendly, but these should be fairly straightforward tasks for another day.

Categories: Random.

Tags: , , , , , , ,

Installing and Configuring Microsoft System Center Configuration Manager

I know that I’ve left the Active Directory series with barely enough info to get you going with Linux, but real life priorities are intervening at work, and I don’t have the time right now to document the next step in the Linux integration, which will be getting service level authentication working with Kerberos for stuff like NFS. It’s coming soon, I promise.

In the meantime, I’m setting up another Microsoft System Center Configuration Manager server — for obvious reasons, I won’t use the full title again, and will call it ConfigMgr or SCCM as the mood strikes me — and I thought I’d do a quick write-up on that while I’m at it; while this installation is going smoothly (fingers crossed) I think I reinstalled my original ConfigMgr server four to six times before getting it right.

ConfigMgr Server Setup

To keep things simple, I’ve kept all of my ConfigMgr-related features on one server — the Certificate Server, SQL Server, IIS, and all of ConfigMgr’s own functions are on one box. It seems to handle the load just fine, though the admin interface can be a little sluggish at times when dealing with heavy database interaction.

First of, there’s a question of the version of Windows to use. While I’m almost always inclined to use the latest version of software that is available, my experiences a year or so ago with ConfigMgr on Windows Server 2008 were less than stellar. I spent quite a lot of time running into inexplicable dead ends, and time to waste is not a luxury I have right now. So I’ve decided to continue with Windows Server 2003 R2, which is the next best thing. Once all my new systems are up in production, I’ll go back to the lab and see if the situation with ConfigMgr on 2008 has improved.

The edition of 2003 also matters — you need at least one Enterprise Edition server in your domain running AD Certificate Services, or you won’t be able to even install ConfigMgr without a lot of ugly hacks — it requires a custom certificate template for the SCCM site servers, and Standard Edition won’t let you use custom templates. Also, though I installed 2003 R2, I didn’t ever actually go through the R2 specific features; I didn’t need them on this particular server.

One the system is built, joined to a domain, and patched, I installed IIS. BITS and WebDAV are important requirements, so make sure to install them. Once these were installed, I went back into the Windows Component wizard and installed Certificate Services. I set it up as an enterprise root CA — if you’re deeply concerned about the security of your PKI, you probably don’t want the root CA to be on a production system, but rather on a box which stays offline and physically secure. However, the only purpose of this particular PKI is to provide security for my ConfigMgr setup, so I didn’t see much point in investing significant resources in making it more secure than the overall Active Directory domain which it plays a comparatively small part in. Next I configured the CA and enrolled the ConfigMgr server for its Site Server certificate. This is a bit of an arbitrary and tedious process, so I’ll just point you to the KB article that describes the process. The IIS server should also be configured with a certificate to use for https:// communications — I used a commercial certificate, but you can also use your local PKI to create one. If you choose the latter, domain clients will automatically trust the cert, but non-domain machines will not. This is probably not a big deal, since any non-domain ConfigMgr clients will have to have the PKI root certificate installed anyway, but it’s worth noting in case you’re using standalone systems with the web interface.

The next thing to install is SQL Server; I used 2005, again primarily because of a lack of testing time; when I first installed ConfigMgr, 2005 was the latest version, so I know that it works. Service Pack 2 is definitely required, and there’s a hotfix that I needed; your mileage may vary. WSUS must also be installed, although it doesn’t have to be used for ConfigMgr to deploy patches; I installed it along with SP1, and then took a break from software installations to catch up on patches through Windows Update; a couple of reboots later I was ready to keep forging on ahead, and we were finally done with prerequisites and ready to tackle ConfigMgr itself.

Well, almost ready; technically, extending the AD schema to enhance ConfigMgr support is not required, but it does improve the efficiency of your site, so I decided to go for it; Microsoft has several TechNet articles with more information on why and how to extend the schema — it was completely painless in my case. There wasn’t much to the actual SCCM installer once I’d gotten all of the prereqs out of the way — I chose to do a native mode installation, enabled all agents except for Network Access Protection (again, a feature I haven’t had time to experiment with in a lab), and that was pretty much it. once it had the information it needed, the installer took quite a while to run, so I suggest locking the console and stepping out for a bit of your preferred caffeine source at this point.

After the install itself, I again ran Windows Update, and rebooted, then started up the New Site Roles wizard. A couple of things to keep in mind — the FQDN for inter- and intra- site communication needs to match the Active Directory domain; this might not be an issue for you, but in my environment most of our servers have internet-facing DNS addresses which are shorter that their fully-qualified AD DNS domain. Since the local PKI will be securing the communications, the AD domain name is the one which will be used, and mismatches definitely won’t be accepted. I also instructed the wizard to “use this server as the active software update point”, and checked the boxes for the following roles, keeping with my “one server to rule them all” philosophy:

  • Server locator point
  • Reporting point
  • Software update point
  • Fallback status point

The next configuration step was to enable inter- and intra- site communication on both the Management Point and Distribution Point. I also created two domain accounts — one user account with minimal access, which ConfigMgr uses to authenticate when pulling data from the server (the Network Access Account) and one with domain admin which is used to do automated installs; strictly speaking, this account might only need local admin on all of your systems, but it seemed easier to me just to go with domain admin and not worry about the one-off problems that might occur with systems not getting the memo about the account’s access. I also added the ConfigMgr server’s computer account (SERVERNAME$) to the domain admins group — this allows the server, which runs as LocalSystem, to do pretty much anything it wants. This may seem scary, but again, keep in mind that as long as you practice the same level of physical and electronic security measures on this server as you do on your domain controllers, you’re really not introducing a new level of vulnerability, you’re just confronting it from a different angle. Like your domain controllers, this server now holds the keys to your electronic kingdom, so treat it with respect and care! The last set of ConfigMgr settings I had to muck with was under the Software Update Point properties:

  • Active software point on site server
  • Allow both inter- and intra- communication
  • Checkmark “All Classifications”
  • Enable synchronization on a schedule
  • Uncheck all languages except English

Obviously the last item was specific to my locale; adjust accordingly. At this point, if you’ve enabled client certificate enrollment (as described in the KB article above) and you enable Active Directory discovery and some form of client installation (I do scheduled pushes through ConfigMgr) you should start to get domain clients joining your system.

Manual Client Installation

Manual client installation is a bit of a hassle, but it’s great for supporting users who don’t live on your domain, whether they be home users, laptops, or for little pockets of machines that have to be managed but just can’t be in your primary Active Directory hierarchy.

Before you try to sign up clients, you’ll need to get them into your PKI; the normal Computer certificate isn’t ideal, since it doesn’t allow you to specify the client’s name. Mimic the steps required to create the ConfigMgr Site Server certificate template, but don’t make any changes to the template other than setting the Subject Name to be supplied in the request.

The first thing to do on each client is to get the PKI root certificate into your computer’s local certificate store. If you use the local PKI for IIS on the ConfigMgr server, then simply browse to https://[fqdn]/certsrv, examine the certificate chain that’s presented, and install the Root CA certificate. Once this is done, log in to the address you just visited with a domain admin account, and create a certificate using the template you just set up above. Make sure that the subject name is unique — it doesn’t have to be the FQDN of the system, nor even to be formatted as a DNS name, but if two systems get certificates with the same subject name, only the most recent system will be able to talk to ConfigMgr. Anyhow, once the certificate is installed, get access to the \SMS_SITE\Client folder — via CD/USB drive, network share, whatever, open a command prompt in the folder, and run the following command:

ccmsetup /forcereboot /noservice /native:FALLBACK SMSSITECODE=[sitecode] CCMHOSTNAME=[server.fqdn] FSP=[server.fqdn]

Be prepared for the system to reboot itself with no warning — if this is unacceptable, omit the /forcereboot switch, but remember that the ConfigMgr client probably won’t run until the system is rebooted.

Categories: Random.

Tags: , , , , ,

Part 3: Windows Server 2008 Active Directory Kerberos and LDAP Integration — Linux User Account Setup

This is the third post in a series on AD integration; the previous post deals with Active Directory installation and configuration

The Linux client I’ve chosen for the test lab is a 64-bit Fedora 10 installation. This distribution was chosen for its compatibility with my deployed systems. Like the Windows 2008 server, this machine is a VM, and the installation was a pretty generic GUI install. My deployed systems are installed via kickstart and managed with Puppet, but to maintain control over the environment, all test lab installation and configuration was done manually.
DHCP and DNS
Once the initial installation was done, I logged in as root and began to configure the system. Although most of my client systems are static IPs, I’ve decided to try using DHCP. The first configuration change I’ve made is to set the hostname in /etc/sysconfig/network, adding a line:

HOSTNAME=<client>

and to ask the system to send it’s hostname with DHCP requests in /etc/sysconfig/network-scripts/ifcfg-eth0:

DHCP_HOSTNAME=<client>

After setting both of these, I rebooted the system to have it pick up the new name. Once the system was back up, I logged in to verify it’s hostname, and checked on the DNS server to verify that it was registered in
Network Time Protocol
Kerberos requires clients to be reasonably closely synchronized to the server; NTP is the obvious choice for this function, as it’s enabled by default in Windows server, and can be set up on fedora clients with the following commands:

echo ’server <ad>’ > /etc/ntp.conf
chkconfig ntpd on
service ntpd start

Shortly after turning ntpd on, notifications should appear in /var/log/messages regarding the state of time synchronization.
In the test lab, I found that the NTP clients didn’t respond well to NTP synchronization; this is a known issue with VMware virtual machines, so I’ve disabled NTP and rely on VMware tools to keep the time sync’d.
OpenLDAP
OpenLDAP configuration is handled by /etc/ldap.conf, which so far has been the trickiest bit of configuration to figure out, so mention of a couple of troubleshooting steps is in order. First, I found it useful to install the openldap-clients package, which includes the ldapsearch utility that can be used for making arbitrary queries. Secondly, appending the line

debug 1

to /etc/ldap.conf provides a wealth of information that has been useful in troubleshooting connections — the output is sent to your terminal when using ‘finger’, ‘id’, or ‘getent’. Higher debug levels can be chosen, but for my purposes 1 was usually sufficient.
But back to /etc/ldap.conf; mine looks like this:
uri ldaps://<ad server>:636/
base dc=fqdn,dc=com
ssl start_tls
ssl on
tls_cacertdir /etc/openldap/cacerts
nss_initgroups_ignoreusers root,ldap,named,\
avahi,haldaemon,dbus,radvd,tomcat,radiusd,\
news,mailman,nscd,gdm,polkituser
nss_base_password ou=people,?sub
nss_base_shadow ou=people,?sub
nss_base_group ou=groups,?sub?&(objectCategory=group)(gidnumber=*)
nss_map_objectclass posixAccount User
nss_map_objectclass shadowAccount User
nss_map_attribute uid sAMAccountName
nss_map_attribute homeDirectory unixHomeDirectory
nss_map_attribute shadowLastChange pwdLastSet
nss_map_objectclass posixGroup group
nss_map_attribute uniqueMember member
#debug 1
The ‘nss_initgroups_ignoreusers’ line is quite important — without it, Fedora 10 will fail to boot properly into GUI mode. Note also the reference to /etc/openldap/cacerts — this directory needs to have a copy of the root certificate for your PKI, whether it’s internal or third-party.
Name Service Switch
Configuration of NSS is pretty straightforward; /etc/nsswitch.conf should have the passwd, shadow, and group lines modified:

passwd: files ldap
shadow: files ldap
group: files ldap

At this point, you should be able to list user accounts — try ‘getent passwd’ and look for your Active Directory accounts at the bottom of the list; if you don’t see them, then uncomment the debug directive in ldap.conf, and see if the additional info gives you any clues. ‘ldapsearch -x’ may also provide some clues.
Kerberos 5
Kerberos deals with realms, which are kerberos administrative regions, and domains, which are DNS domains. By convention, realms are referred to in uppercase,
/etc/krb5.conf:
[libdefaults]
default_realm = FQDN.COM
ticket_lifetime = 24h
forwardable = yes
[realms]
FQDN.COM = {
kdc = <ad server>:88
}
[domain_realm]
fqdn.com = FQDN.COM
.fqdn = FQDN.COM
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
forwardable = true
krb4_convert = false
}
Pluggable Authentication Modules
/etc/pam.d/system-auth:
auth required pam_env.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 500 quiet
auth sufficient pam_krb5.so use_first_pass
auth required pam_deny.so
account required pam_unix.so broken_shadow
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid <>
account [default=bad success=ok user_unknown=ignore] pam_krb5.so
account required pam_permit.so
password requisite pam_cracklib.so try_first_pass retry=3
password sufficient pam_unix.so sha512 shadow \
nullok try_first_pass use_authtok
password sufficient pam_krb5.so use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
session [success=1 default=ignore] pam_succeed_if.so \
service in crond quiet use_uid
session required pam_unix.so
session optional pam_krb5.so
Once all of these changes have been made, Active Directory users should be able to authenticate on the new system. Since I use auto-mounted home directories, I don’t have PAM creating home directories for new users, but that’s not difficult to set up; look into the mkhomedir.so module if this sounds useful in your environment.

Categories: Random.

Tags: , , , , , , , , , ,