Parsing Media RSS with PHP SimpleXML

Parsing XML docs with PHP SimpleXML is pretty straightforward. Yesterday i lost around 5 minutes to parse a Media RSS XML, and that was weird because normally with SimpleXML you take like 30 seconds… A Media RSS (MRSS) document is just a RSS with media extensions:

<rss version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>RSS Title</title>
    <link>http://www.domain.com/mylink</link>
    <description>My description</description>
    <item>
      <title>Title item 1</title>
      <link>http://www.domain.com/item_1.html</link>
      <description>Item 1 description</description>
      <guid>http://www.domain.com/item_1.html</guid>
      <media:content url="http://www.domain.com/item_1.jpg" height="240" width="320" />
    </item>
    <item>
      <title>Title item 2</title>
      <link>http://www.domain.com/item_2.html</link>
      <description>Item 2 description</description>
      <guid>http://www.domain.com/item_2.html</guid>
      <media:content url="http://www.domain.com/item_2.jpg" height="240" width="320" />
    </item>
    .... etc 
  </channel>
</rss>

The “problem” is to access the media:content or the other media:* elements. But don’t worry I’m going to show you how to do it 🙂

$xml = simplexml_load_file('http://domain.com/mrss.xml');
$namespaces = $xml->getNamespaces(true); // get namespaces

// iterate items and store in an array of objects
$items = array();
foreach ($xml->channel->item as $item) {

  $tmp = new stdClass(); 
  $tmp->title = trim((string) $item->title);
  $tmp->link  = trim((string) $item->link);
  // etc... 
  // now for the url in media:content
  //
  $tmp->media_url = trim((string) 
                    $item->children($namespaces['media'])->content->attributes()->url);

  // add parsed data to the array
  $items[] = $tmp;
}

There, a piece of cake!

UPDATE

I received a comment about Picasa RSS feed, where you have to dig just a bit deeper, as the media:url is inside a media:group. The XML feed is as follows

<?xml version='1.0' encoding='UTF-8'?>
<rss xmlns:atom='http://www.w3.org/2005/Atom' 
xmlns:media='http://search.yahoo.com/mrss/' 
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' version='2.0'>
  <channel>
    <atom:id>https://picasaweb.google.com/data/feed/base/user/103218581909188195000</atom:id>
    <lastBuildDate>Wed, 16 Apr 2014 07:28:42 +0000</lastBuildDate>
    <title>Galerie fotografií uživatele Jiřetín JINAK</title>
    .... etc
    <item>
      <pubDate>Thu, 10 Apr 2014 07:16:22 +0000</pubDate>
      <atom:updated>2014-04-16T07:28:42.202Z</atom:updated>
      <author>Jiřetín JINAK</author>
      .... etc
      <media:group>
        <media:content url='https://lh6.googleusercontent.com/-C6WmXjRnV8Y/U0ZFRnm-ujE/AAAAAAAAAPQ/AbwIc0Ycugk/s100-c/RizikovaMistaVHornimJiretine.jpg' type='image/jpeg' medium='image'/>
        <media:credit>Jiřetín JINAK</media:credit>
        <media:description type='plain'/>
        <media:keywords/>
        <media:thumbnail url='https://lh6.googleusercontent.com/-C6WmXjRnV8Y/U0ZFRnm-ujE/AAAAAAAAAPQ/AbwIc0Ycugk/s160-c/RizikovaMistaVHornimJiretine.jpg' height='160' width='160'/>
        <media:title type='plain'>Riziková místa v Horním Jiřetíně</media:title>
      </media:group>
    </item>
    .... etc
  </channel>
</rss>

The PHP code follows the same logic, just add another step to take into account media:group

$xml = simplexml_load_file('http://picasaweb.google.com/data/feed/...&prettyprint=true');
$namespaces = $xml->getNamespaces(true); // get namespaces

$items = array();
foreach ($xml->channel->item as $item) {

  $tmp = new stdClass();
  $tmp->title = trim((string) $item->title);
  $tmp->link  = trim((string) $item->link);
  // etc...

  // now for the data in the media:group
  //
  $media_group = $item->children($namespaces['media'])->group;

  $tmp->media_url =    trim((string)
                       $media_group->children($namespaces['media'])->content->attributes()->url);
  $tmp->media_credit = trim((string)
                       $media_group->children($namespaces['media'])->credit);
  // etc

  // add parsed data to the array
  $items[] = $tmp;
}

DNS server with djbdns (VegaDNS GUI + bonus replication)

djbdnsThis is my personal guide to setup djbdns tinydns DNS server to publish domain name information to the Internets. If djbdns, tinydns, name server, dns are strange words just move along, move along…

Continue reading “DNS server with djbdns (VegaDNS GUI + bonus replication)”

Setting up SSH access with server keys (no password) in FreeBSD

You want to login from user@host-a to user@host-b automatically (with no password prompt).

Install ssh-copy-id in the host-a. This little handy script takes care of all the dirty details from your back.

cd /usr/ports/security/ssh-copy-id
make install clean

then run (still in host-a)

ssh-copy-id user@host-b

If you get the error “ERROR: No identities found” then you must generate your keys first and then re-run ssh-copy-id. To generate a pair of private/public SSH keys you must issue:

mkdir ~/.ssh
chmod 700 ~/.ssh
ssh-keygen -t rsa

You can leave the pass-phrase empty for automatic logins (no password prompt) or the more secure but less practical password prompt each and every time that you use the newly generated keys.

Next login from host-a to user@host-b will be made with SSH keys.

The dirty details

  • the user@host-a public key, usually ~/.ssh/id_rsa.pub is copied to host-b user/.ssh/authorized_keys
  • the host-b public host key (/etc/ssh/ssh_host_rsa_key.pub) is copied to host-a known hosts

FreeBSD migrating user accounts

FreeBSDQuick and dirty way to migrate user accounts from one FreeBSD box to another. From the source box you will only need two files:

/etc/master.passwd
/etc/group

copy them to the target box, but DON’T overwrite (yet) the existing files there. Put them in /root/master.passwd and /root/group. Compare the copied files with the existing ones for new system users that may exist. If there are any new users/groups add them to the copied files.

Then move /root/group to /etc/group and run this magical command

pwd_mkdb -p /root/master.passwd

It will install in /etc/master.passd and recreate all the needed files (/etc/pwd.db, /etc/spwd.db and /etc/passwd).

Securing SSH with SSHGuard

SSHGuardIf you have a remote server running some flavor of Unix or Linux 99% chances that you use SSH. The best security practice is to use an access key with password and disable password access altogether. But you end up loosing some flexibility (for some customers Putty is this utterly complex piece of software, imagine them playing with SSH keys…).

The best you can do is to enforce a better user password policy, but even so, as every password service it’s at mercy of brute force attacks. These attacks consume precious clock cycles and worst case scenario they can break a password and gain access to the system.

So, here comes SSHGuard to our rescue. It’s a pretty neat piece of software that is highly flexible and customizable to ones system, needs and paranoia level. On top of that is maintenance free and very easy to setup.

I’m using FreeBSD and the venerable (yet, very capable) IPFW firewall. The choice of the firewall is simply because it’s the one that i am more pro-efficient with.

First thing is to enable IPFW on your system. Open /etc/rc.conf and add these lines

firewall_enable="YES"
firewall_type="open"

actually this setup is only to bring IPFW up, it doesn’t filter anything, all the traffic is passed trough. But if you forget the firewall_type=”open” rule and start the firewall you will be lock out, because the default is no traffic allowed… (and you win a drive to the data-center or some kind of remote rescue shell procedure).

Start IPFW

/etc/rc.d/ipfw start

and check that is running

ipfw show

Now, you are ready to install SSHGuard itself, very easy task

cd /usr/ports/security/sshguard-ipfw
make install clean

and enable it in /etc/rc.conf

sshguard_enable="YES"

Ready? Start it

/usr/local/etc/rc.d/sshguard start

Still, there is a final thing to take care. SSHGuard uses syslogd to monitor incoming (failed) logins. So, you must edit /etc/syslog.conf and uncomment (or add if it’s not there) the line that the SSHGuard port added.

auth.info;authpriv.info     |exec /usr/local/sbin/sshguard

And restart syslogd

/etc/rc.d/syslogd restart

And now your SSH service should be bullet proof to brute force attacks. Keep safe!

UPDATE 2014-02-23

Latest versions of SSHGuard don’t use syslogd any more, it uses an internal “log sucker” that follows the logs. The default logs are “/var/log/auth.log:/var/log/maillog”, as I don’t want it to follow /var/log/mailog i override this in /etc/rc.conf with:

sshguard_watch_logs="/var/log/auth.log"

UPDATE 2014-08-05

For several reasons, I have switched from IPFW to PF. So the port to install is /usr/ports/security/sshguard-pf/ and you must add this line to your /etc/pf.conf and enable PF in /etc/rc.conf

table  persist

then to list the blocked IPs

pfctl -t sshguard -T show

to remove an IP from the list

pfctl -t sshguard -T delete aaa.bbb.ccc.ddd

to remove all the IPs

pfctl -t sshguard -T flush

NOTE

Also going to test drive on a debian box fail2ban, and will soon post quick review and differences, drawbacks, benefits versus sshguard.