Advertise with Googlier.com Allan Feid - script https://allanfeid.com/category/tags/script en Cluster Management with FreePanel https://allanfeid.com/content/cluster-management-freepanel <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><p>I haven't had much time to continue work on <a href="http://freepanel.org" title="FreePanel Wiki">FreePanel</a>, but the Perl modules I've written have proven to be useful in cluster management. Take a look at the following diagram as an example scenario:</p> <div align="center"><img src="/sites/default/files/cluster_diagram.png" title="Web Cluster Example" alt="Web Cluster Example" /></div> <p>There's two key points to take away from that diagram.</p> <ul><li>physical servers are load balanced using a single VIP (Virtual IP)</li> <li>the servers share access to a single storage mount</li> </ul><p>The idea behind load balancing is that requests go to the individual servers based on which can handle the traffic. A hardware appliance takes care of the actual load balancing work, but this could be done using some software just as easily.</p> <h2>So what does this have to do with FreePanel?</h2> <p>If you're constantly needing to add additional domains to your cluster, doing this manually becomes tiresome, especially when in most cases your application needs the same Apache configuration per domain. Take the below basic Virtual Host config:</p> <pre> <virtualhost> ServerAdmin webmaster@example.com ServerName example.com ServerAlias www.example.com ErrorLog /home/sites/logs/example.com.error CustomLog /home/sites/logs/example.com.access combined DocumentRoot /home/sites/example.com/web <directory></directory> Options Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all </virtualhost></pre><p>Each of your 50 or so hosted applications needs a similar configuration, just with a different domain name. That's where FreePanel can make your life easier. Take the above example and we'll form a template that can be used with FreePanel's Apache module.</p> <pre> <virtualhost>&gt; ServerAdmin webmaster@<domain> ServerName <domain> ServerAlias www.<domain> ErrorLog /home/sites/logs/<domain>.error CustomLog /home/sites/logs/<domain>.access combined DocumentRoot /home/sites/<domain>/web <directory>/web/&gt; Options Indexes FollowSymLinks AllowOverride None Order allow,deny Allow from all </directory></domain></domain></domain></domain></domain></domain></virtualhost></pre><p>As you can expect, this module can parse the template file and replace certain items with what arguments are being passed. We'll take a look at the addSite method of <a href="http://github.com/crazed/freepanel/blob/master/run/FreePanel/Admin/Apache.pm" title="FreePanel::Admin::Apache on github">FreePanel::Admin::Apache</a>.</p> <pre> sub addSite { my ($self, $domain, $ip_addr) = @_; $self-&gt;logger("function: addSite($domain, $ip_addr) called.", $self-&gt;FUNC_CALL); my $vhost_dir = $self-&gt;getVhostDir(); my $vhost_templ = $self-&gt;getVhostTemplate(); my $check = $self-&gt;getValidateObj(); if ($check-&gt;is_active($domain, $vhost_dir)) { $self-&gt;logger("vhost config already exists for $domain.", $self-&gt;ERROR); return 0; } open (VHOST, '&gt;', $vhost_dir."/$domain"); open (VHOST_T, ') { s/<domain>/$domain/; s/<ip_addr>/$ip_addr/; print VHOST; } } else { while (<vhost_t>) { s/<domain>/$domain/; s/<ip_addr>/\*/; print VHOST; } } close VHOST_T; close VHOST; return 1; } </ip_addr></domain></vhost_t></ip_addr></domain></pre><p>Nothing really that crazy going on here, it just opens up the template file -- defined in your config file -- then replaces <code class="prettyprint">&lt;DOMAIN&gt;</code> and <code class="prettyprint">&lt;IP_ADDR&gt;</code> with the values you pass. This file is written out to a directory -- also defined in the config file -- and if no IP address is passed, we default to use *. This could be easily modified to include additional values, but I won't go into that now.</p> <h2>Okay, how do I use it?</h2> <pre> #!/usr/bin/perl use strict; use FreePanel::Admin::Apache; if ($#ARGV [ip address]\n"; exit; } my $domain = $ARGV[0]; my $ip_addr = $ARGV[1]; my $apache = new FreePanel::Admin::Apache; $apache-&gt;addWebDir($domain) or die "Error adding web directory for $domain"; $apache-&gt;logger("web directory for $domain has been created.", $apache-&gt;INFO); if ($ip_addr) { $apache-&gt;addSite($domain, $ip_addr) or die "Error creating vhost with $ip_addr for $domain"; $apache-&gt;logger("vhost config created for $domain using $ip_addr.", $apache-&gt;INFO); } else { $apache-&gt;addSite($domain) or die "Error creating vhost for $domain"; $apache-&gt;logger("vhost config craeted for $domain.", $apache-&gt;INFO); } # restart apache, still implementing FreePanel::Admin::Apache-&gt;restart system('apachectl restart'); </pre><p>With the above you have a basic script for adding new domains fairly quickly. This will also allow you to keep track of what has been added and when since everything you do using FreePanel is logged to a file you chose. Here's a sample log output:</p> <pre> 03-07-2010 11:59:36 :: [ DEBUG ] function: addWebDir(example.com) 03-07-2010 11:59:36 :: [ INFO ] web directory for example.com has been created. 03-07-2010 11:59:36 :: [ DEBUG ] function: addSite(example.com) called. 03-07-2010 11:59:36 :: [ INFO ] vhost config craeted for example.com. </pre><p>Your level of logging is also defined by the configuration file. So with this basic script you get a log keeping track of what sites have been added and when as well as taking on the tedious work of copying a configuration file and switching a few values to meet the new domain's needs. There's a bit of documentation lacking, but some will soon be added to the <a href="http://freepanel.org" title="FreePanel.org">FreePanel Wiki</a>.</p> <h2>A case study: three web servers (clustered) using SSL for all domains</h2> <p>Recently, I was dealing with a client that had three web servers all behind a load balancer (Cisco CSS). This was a basic CRM software that required SSL for each virtual host (domain). This was troublesome because you need a unique IP address for each SSL site, and since all of their clients were using SSL, this meant every single domain had to have its own IP. This also means that each virtual host will be listening on a private IP behind the CSS. What this means is you will see something like this:</p> <p><strong>Web01:</strong></p> <pre> <virtualhost> ServerAdmin webmaster@example.com ServerName example.com ... </virtualhost></pre><p><strong>Web02:</strong></p> <pre> <virtualhost> ServerAdmin webmaster@example.com ServerName example.com ... </virtualhost></pre><p><strong>Web03:</strong></p> <pre> <virtualhost> ServerAdmin webmaster@example.com ServerName example.com ... </virtualhost></pre><p>Being a lazy systems admin, I certainly did not want to manually create three configurations each time I wanted to add a new domain. So I used <code class="prettyprint">FreePanel::Admin::Apache</code> and some ssh keys to carry out the creation of each. Using the <code class="prettyprint">addSite</code> method, I passed the last octet of each domain rather than the full IP, and had the template using something similar to this:</p> <pre> <virtualhost>&gt; </virtualhost></pre><p>Of course, changing the first three octets based on which server this template was being used on. I had a simple script similar to the one above that took the input for domain name and IP address, and a main script which I ran from an admin box. The main script was in charge of creating SSH connections and launching the helper script on each server with the proper arguments. This main script also remotely restarted Apache after creating each site.</p> <p>While this example certainly isn't very elegant as password-less root SSH keys were required, it did however prove to be a very useful use-case for our Apache module. Solutions to problems such as this are why I work on FreePanel, because we all know that lazy syadmins are usually the best sysadmins.</p> </div></div></div><section class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Tags:&nbsp;</h2><ul class="field-items"><li class="field-item even"><a href="/category/tags/script" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">script</a></li><li class="field-item odd"><a href="/category/tags/perl" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">perl</a></li><li class="field-item even"><a href="/category/tags/freepanel" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">FreePanel</a></li><li class="field-item odd"><a href="/category/tags/sysadmin" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">sysadmin</a></li></ul></section> Fri, 05 Mar 2010 17:16:09 +0000 Allan Feid 62 at https://allanfeid.com Perl: wrapper for virt-install to provide iSCSI storage pool creation https://allanfeid.com/content/perl-wrapper-virt-install-provide-iscsi-storage-pool-creation <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><p>So I've been working on a setup for virtualization, where currently I'm using <a href="/content/serving-iscsi-targets-your-zpool" title="Serving iSCSI targets from your zpool">one large iSCSI target</a> from an OpenSolaris machine. It occurred to me that there is a downside to this. I cannot use zfs snapshots for individual machines and backups/restores will be more difficult. I also did not want to be hassled with having to connect the iscsi targets using <code class="prettyprint">iscsiadm</code> every time I spin up a new VM.</p> <p>Luckily libvirt <a href="http://libvirt.org/storage.html#StorageBackendISCSI" title="libvirt storage pool management">provides iSCSI storage pool methods</a> which will connect the targets and manage them for you. This makes life much easier for me, however, <code class="prettyprint">virt-install</code> does not have the functionality to install on these. Me being more proficient in Perl, I decided to take up the task of creating a simple wrapper. To use it, first you need this template XML file called "disk.xml."</p> <pre> <pool type="iscsi"><name>$NAME</name> <pre class="prettyprint"><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;host name="$PORTAL"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;device path="$TARGET"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </code></pre> <target><path>/dev/disk/by-path</path></target></pool></pre><p>Here's my script that I call <code class="prettyprint">virt-iscsi-install</code>, and it's pretty easy to use. Not the greatest solution as it does not do much for error checking, but for now this is what I'll be using. I may consider venturing into some Python and patch virt-install to include similar functionality as the libvirt library is well documented and not hard to understand.</p> <pre> #!/usr/bin/perl use Sys::Virt; use Getopt::Long; usage() if ( ! GetOptions( 'name|n=s' =&gt; \$name, 'ram|r=s' =&gt; \$ram, 'target|t=s' =&gt; \$target, 'portal|p=s' =&gt; \$portal, 'mirror|m=s' =&gt; \$mirror )) ; #print "name: $name\nram: $ram\ntarget: $target\nportal: $portal\n"; sub usage { print "usage: $0 --name <name> --ram <ram> --target ". "<iscsi target=""> --portal <iscsi portal=""> --mirror <install mirror="">\n"; exit; } my $path = createPool($target,$portal,$name); print "Volume: $path\n"; print "Launching virt-install..\n"; system ("virt-install -n $name -r $ram --location $mirror --file $path --vnc --bridge=br0"); sub createPool { my ($target, $portal, $name) = @_; my $vmm = Sys::Virt-&gt;new(address =&gt; 'qemu:///system'); open my $fh, ') { s/\$NAME/$name/; s/\$TARGET/$target/; s/\$PORTAL/$portal/; $xml = "$xml $_"; } close $fh; #print $xml; my $pool = $vmm-&gt;define_storage_pool($xml); $pool-&gt;create(); if (!$pool-&gt;get_autostart) { print "setting pool to auto-start\n"; $pool-&gt;set_autostart(1); } my @volNames = $pool-&gt;list_volumes; foreach my $vol (@volNames) { return $vol-&gt;get_path; } } </install></iscsi></iscsi></ram></name></pre></div></div></div><section class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Tags:&nbsp;</h2><ul class="field-items"><li class="field-item even"><a href="/category/tags/script" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">script</a></li><li class="field-item odd"><a href="/category/tags/perl" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">perl</a></li><li class="field-item even"><a href="/category/tags/libvirt" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">libvirt</a></li></ul></section> Wed, 03 Mar 2010 16:42:28 +0000 Allan Feid 60 at https://allanfeid.com Parse Airodump-NG Output with Perl https://allanfeid.com/content/parse-airodump-ng-output-perl <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded">I had to enumerate all the wireless access points around school today. So I booted up BackTrack 4 on my Eee PC and ran airodump-ng to create a list of ESSIDs, their corresponding BSSIDs, and the channels they were running on. <!--break--> Start off by running airodump-ng to save the data: <pre>airodump-ng ath0 -w enum</pre> That saves all the data to enum-01.txt, .csv, and .cap files. The .txt file itself is difficult to read, so I wanted to make a perl script to parse the data to a much more readable format. This is what I came up with: <pre> #!/usr/bin/perl -w my @data; my @bssid; my @fts; my @lts; my @channel; my @speed; my @privacy; my @cipher; my @authentication; my @power; my @num_beacons; my @num_ivs; my @lan_ip; my @idlen; my @essid; # read the airodump file open FILE, "<? echo htmlspecialchars("<enum.txt"); ?>" or die $!; while (<? echo htmlspecialchars("<FILE>"); ?>) { # lines are put in default $_ var, split reads this by default @data = split(/,\s*/); push(@bssid, $data[0]); push(@fts, $data[1]); push(@lts, $data[2]); push(@channel, $data[3]); push(@speed, $data[4]); push(@privacy, $data[5]); push(@cipher, $data[6]); push(@authentication, $data[7]); push(@power, $data[8]); push(@num_beacons, $data[9]); push(@num_ivs, $data[10]); push(@lan_ip, $data[11]); push(@idlen, $data[12]); push(@essid, $data[13]); } close(FILE); # get unique essids my $essid_search; # easier to remember/type than $essid[$i]; my @unique_essids; # array of unique essids my $unique_essid; # var used in foreach loop as the current unique essid my $check_unique; # bool, if true (1) essid is unique foreach ($i = 0; $i <= scalar(@essid); $i++) { $essid_search = $essid[$i]; # if blank essid go to next if (!$essid_search) { next; } # loop through and see if it's a unique essid $check_unique = 1; foreach $unique_essid (@unique_essids) { if ($essid_search eq $unique_essid) { $check_unique = 0; last; } } # if unique, add it to @unique_essids if ($check_unique == 1) { push(@unique_essids, $essid_search); } } # look at all the essid, then print out corresponding bssid and channel my $essid_match; foreach $essid_match (@unique_essids) { print "Matching for $essid_match\n"; print "-----------------------------------------------------\n"; print "\tBSSID\t\t\tCHANNEL\tPRIVACY\n"; print "-----------------------------------------------------\n"; for ($i = 0; $i <= scalar(@essid); $i++) { if (($essid[$i]) && ($essid[$i] eq $essid_match)) { print "\t".$bssid[$i]."\t".$channel[$i]."\t".$privacy[$i]."\n"; } } print "=====================================================\n\n\n"; } </pre> Nothing fancy going on here and you'll have to change the open FILE section to correlate to the name of your capture file. This also ignores blank ESSIDs, as for my project I did not need them. Either way quick and easy and much better looking. I'll probably expand on this for future use but this is where I'll leave it for now.</div></div></div><section class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Tags:&nbsp;</h2><ul class="field-items"><li class="field-item even"><a href="/category/tags/script" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">script</a></li><li class="field-item odd"><a href="/category/tags/wireless" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">wireless</a></li><li class="field-item even"><a href="/category/tags/perl" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">perl</a></li><li class="field-item odd"><a href="/category/tags/airodump-ng" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">airodump-ng</a></li></ul></section> Wed, 01 Apr 2009 03:32:51 +0000 Allan Feid 38 at https://allanfeid.com Automated Backup w/ rsync over SSH https://allanfeid.com/content/automated-backup-w-rsync-over-ssh <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><p>There are four main sections to getting this up and running</p> <ol> <li><a href="#intro">Introduction</a></li> <li><a href="#install">Installing/Configuring Required Software</a></li> </ol> <ul> <li>OpenSSH</li> <li>rsync</li> <li>smbfs (optional)</li> </ul> <li><a href="#windows">Auto-mounting a Windows share (optional)</a></li> <li><a href="#script">Creating a script to do the work for you</a></li> <li><a href="#auto">Automating the backup using cron jobs</a></li> <p><a name="intro" id="intro"></a><strong>Introduction</strong> </p> <p>Before going over everything here, I'd like to point out why this information is useful and define a few terms. Using rsync to create a backup server solution is entirely free and conserves bandwidth. The reason it conserves bandwidth is rsync itself only sends the parts of each file that have changed. FTP sends the entire file each time even if only one word was changed in a file. Sending all of this traffic over SSH ensures security and also protects you from having to run a full rsync server. Key Terms:</p> <ul> <li>Backup server: the PC that will be issuing all the rsync commands and storing the backups</li> <li>Host computer: the PC where we will be getting the files from</li> </ul> <p>With all of that out of the way, it's just a matter of setting everything up. </p> <p><a name="install" id="install"></a><strong>Installing/Configuring Required Software</strong></p> <p>In order to get things going you'll need to install a few pieces of software. In my situation I had a Windwos share that I wanted to back my home folder up to. In order to play around with Windows shares you'll need to also install the samba client. </p> <p><strong>OpenSSH</strong> </p> <p>Simple to install with one line:<br /> <code>sudo apt-get install openssh-server</code></p> <p>Once this is done you will be able to connect but it requires a password entry. This is sort of a problem when we're looking at automating the entire backup process so in order to solve that problem we'll use ssh keys instead. From your backup server we'll need to create an ssh key by issuing the following: </p> <p><code>ssh-keygen -t dsa</code> </p> <p>Now you can either copy the key manually in some fashion or do it over an SSH connection with this: </p> <p><code>scp ~/.ssh/id_dsa.pub username@x.x.x.x:./id_dsa.pub</code></p> <p>Note that <cite>username</cite> is the local account on the host and <cite>x.x.x.x</cite> is the IP of the host. What this will do is copy the public key from the server to our host. After that we need to tell our host PC to use that key as authorization instead of asking for a password.</p> <pre> cd .ssh touch authorized_keys2 chmod 600 authorized_keys2 cat ../id_dsa.pub &gt;&gt; authorized_keys2 rm ../id_dsa.pub </pre><p> Here you can see we just change directory to .ssh and use touch to create the file authorized_keys2. Once the file is created lock it down and only allow the current user to change the file and finally add the public key then remove the original file containing the key. When you finish this try it out. If you're able to connect to your host pc from the backup server over SSH with no password everything has been setup correctly.</p> <p><strong>rsync &amp; smbfs</strong></p> <p>These two are simple to install with just one line:</p> <pre> sudo apt-get install rsync sudo apt-get install smbfs </pre><p> Keep in mind that smbfs is not required it just depends on your setup. If you would like to backup to/from a Windows share then you will want this along with the next section. Otherwise you can skip down to automating the backup using cron jobs.</p> <p><a name="windows" id="windows"></a><strong>Auto-mounting a Windows share (optional)</strong></p> <p>To mount a windows share everytime you boot up you will need to modify your <cite>/etc/fstab</cite> file. You only need to add one line at the end of this file like this:</p> <pre>//x.x.x.x/Backups /media/Backup cifs iocharset=utf8,credentials=/root/.smbcredentials,uid=1000 0 0</pre><p> Of course you must replace <cite>x.x.x.x</cite> with the IP of your Windows computer and "Backups" with the share name. You will also need to create the file <cite>/root/.smbcredentials</cite>.</p> <p><cite>.smbcredentials</cite></p> <pre> username=MyUsername@domain.com password=MyPassword </pre><p> This is a security risk because anyone with root priviledges can read your username/password in plain text so we must lock this down as much as possible. </p> <pre> sudo chown root /root/.smbcredentials sudo chmod 600 /root/.smbcredentials </pre><p> Also as a side note, you won't need "@domain.com" if your PC is not part of a domain. Once all this is down you can mount everything by issuing this:</p> <p><code>sudo mount -a</code></p> <p>You should now be able to access the share by browsing /media/Backups.</p> <p><a name="script" id="script"></a><strong>Creating a script to do the work for you</strong><br /> We're all lazy. Especially with making sure we have up-to-date backups. So to make it easier I've compiled a simple script that will ping the host to make sure it's alive and then execute the rsync command.</p> <p><cite>rsync_backup.sh</cite></p> <pre> #!/bin/bash awk ' function pinger(count,ip) { command = "ping -c "count " " ip while (( command | getline res )&gt; 0 ) { if ( res ~ /0 received|100% packet loss/ ) { close(command) return 100 } } close(command) return 0 } BEGIN { IP="x.x.x.x" if ( pinger(2,IP) == 0 ) { system("rsync -raz --delete --rsh=ssh user@x.x.x.x:/home/user /media/Backup") } }' </pre><p> This will of course need to be modified to work with your setup. Replace <cite>x.x.x.x</cite> with your hosts IP and <cite>/home/user</cite> with the directory you would like to backup.</p> <p><code>rsync -raz --delete --rsh=ssh user@x.x.x.x:/home/user /media/Backup</code></p> <p>Here you can modify the actual rsync command used. The options <cite>r, a, &amp; z</cite> are used for recursive, archive, and compress the data. The delete option deletes files that no longer exist on the source (in this case /home/user) and rsh tells rsync to use ssh. Take a look at the <a href="http://www.samba.org/ftp/rsync/rsync.html" title="Online man page for rsync">man page</a> for further information about available rsync commands.</p> <p><a name="auto" id="auto"></a><strong>Automating the backup using cron jobs</strong></p> <p>To run this script everyday at 11PM you can simply add a cron job.</p> <p><code>crontab -e</code></p> <p>The above starts your default text editor in the file that holds cron jobs. The next line is what should be added to run the script at a certain time everyday.</p> <p><code>0 23 * * * /path/to/rsync_backup.sh</code></p> <p>Take a look at the <a href="http://www.alts.net/servers/extensions/cron/crontab_5.html" title="Online man page for crontab">crontab man page</a> for a more detailed explanation of how cronjobs work.</p> <p><strong>Summary</strong></p> <p>Once this is all setup it's easy to modify to backup multiple PCs from the single backup server. For Windows PCs you can create a share or use the administrative share (\\comp\c$\path\to\files) and auto-mounting in order to back up PCs without installing SSH or rsync. This concept also works for Linux environments but does require a full samba install and configuration. That's the beauty of rsync, you can really do whatever you would like with it. If you have any questions or feel stumped feel free to contact me.</p> <p><a href="/content/my-projects">Back to my projects</a></p> </div></div></div><section class="field field-name-taxonomy-vocabulary-1 field-type-taxonomy-term-reference field-label-above view-mode-rss"><h2 class="field-label">Tags:&nbsp;</h2><ul class="field-items"><li class="field-item even"><a href="/category/tags/linux" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Linux</a></li><li class="field-item odd"><a href="/category/tags/windows" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Windows</a></li><li class="field-item even"><a href="/projects" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">project</a></li><li class="field-item odd"><a href="/category/tags/backup" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">backup</a></li><li class="field-item even"><a href="/category/tags/ssh" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">ssh</a></li><li class="field-item odd"><a href="/category/tags/rsync" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">rsync</a></li><li class="field-item even"><a href="/category/tags/automated" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">automated</a></li><li class="field-item odd"><a href="/category/tags/script" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">script</a></li><li class="field-item even"><a href="/category/tags/samba" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">samba</a></li><li class="field-item odd"><a href="/category/tags/networking" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">networking</a></li></ul></section> Wed, 26 Nov 2008 01:47:52 +0000 Allan Feid 20 at https://allanfeid.com