The first step in securing servers is to ensure that they are physically as secure as possible and and then monitored for unauthorized access  Many times when setting up servers in a small office or co-location facility many people have their systems in a locking cabinet within a moderately secured physical building.  However a determined attacker can usually bypass many physical controls so adding as many layers and monitoring helps can help both to thwart an attacker or be used to monitor for authorized equipment access through change management filtering on your monitoring and alerting system.

One problem with the small office or colo environment is there are very few affordable solutions that can be used to monitor for authorized and unauthorized access.  To solve this problem, ZZ Servers has implemented a cabinet monitoring solution that is able to not only monitor for cabinet door entry but also has the ability to have temperature and humidity sensors (which we do not actually do in this initial proof of concept).

The cabinet door alarm is based on a teensy 2.0 USB device that uses digital inputs to determine if magnetic alarm door switches are open or closed and then monitor their status with any standard monitoring system through a USB connection to the Teensy device. The teensy can be ordered from PJRC for $16. The LED, Resistor and remaining components can be purchased from Jameco, Amazon, Home Depot, Radio Shack or any other similar store.

The teensy is connected through USB to a linux server in the cabinet that runs an application to query the status of each sensor. The teensy provides a +5V pin that will be connected to a 10k ohm resistor which is then connected to the GND with one connection to each of 4 input pins for the door sensor and an LED connected to interface 11.

The current design is for only 4 alarm switches; but there is no reason the other inputs can’t be used. If additional inputs are used then the associated firmware and software programs will need to be updated to reflect the number of interfaces.

This design also expects there to be a closed circuit on all monitored ports otherwise an alarm will be raised. A simple closed loop will work for any alarm switches not installed.

Once the circuit is assembled the firmware needs to be uploaded to the teensy. The firmware used is based on the arduino support for Teensy that can be downloaded.  The steps to setup the Teensy/Arduino development environment are found on the page and need to be followed to allow for proper aduino sketch to be built and loaded onto the teensy flash.

    • Install 49-teensy.rules in /etc/udev/rules.d (see below for contents of this file)
    • Download & Run teensy duino installer. Examples/samples are not needed unless doing future development

Once the development tools are installed:

    • Start the arduino IDE (found in arduino software extract)
    • Connect the teensy usb interface
    • Set board type to Teensy 2.0 (Tools/Board/Teensy 2.0)
    • Load the code (below)
    • Verify (checkbox in IDE) the code
    • Upload (right arrow in IDE) the HEX firmware

Teensy Firmware:
The firmware has 3 main sections; the Header where the various variables are defined that are used within the program, The setup function which runs when the teensy is powered on (plugged into USB) and then the loop which is executed after setup executing the designed function.

When the teensy boots, it load the setup function which initializes the device allowing for INPUT_PULLUP functionality for the 4 pins used for the alarm. This creates the alert when the switch is opened. The setup then initializes the USB serial device at 38400 8n1 and configures the LED output PIN and makes sure the LED is off.

The loop function is the core of the firmware. This is the function that the teensy executes over and over. In this function the first thing to do is read each of the alarm interfaces and if there is an alert flag it so we can be sure to blink the LED. Next the loop will see if there are any requests on the serial port, which will come from the serial program further down in this post. If there is input from the serial interface, the loop confirms it is a valid request [1,2,3,4] and then prints back on the serial interface a simple message showing the status of the serial ports.

Finally the loop ends by running the BlinkLED function if there is an alarm otherwise if the LED is on be sure to turn it off.

The BlinkLED function works by using a nice variable type provided by the Teensy “elapsedMillis” which creates a timer that is used to trace the time since the variable was created. Using this variable if it has been one second (1000ms) then reset the timer and if the LED is on turn it off, otherwise turn it on.
zz_alarm0.ino

// Header Section
int ledPin =  11;
int ledon = 0;
int ALARM_1 = 1;
int ALERT_1 = 0;
int ALARM_2 = 2;
int ALERT_2 = 0;
int ALARM_3 = 3;
int ALERT_3 = 0;
int ALARM_4 = 4;
int ALERT_4 = 0;
int alarmnow = 0;
char alarmcheck = ' ';
elapsedMillis sinceAlarm;
//End Header Section

// The setup() method runs once, when the sketch starts
void setup()   {
     pinMode(ALARM_1, INPUT_PULLUP);
     pinMode(ALARM_2, INPUT_PULLUP);
     pinMode(ALARM_3, INPUT_PULLUP);
     pinMode(ALARM_4, INPUT_PULLUP);
     Serial.begin(38400);
     pinMode(ledPin, OUTPUT);
     digitalWrite(ledPin, LOW);
}

// the loop() method runs over and over again, checking for events
void loop()   {
     alarmnow = 0;
     alarmcheck = ' ';

     ALERT_1 = digitalRead(ALARM_1);
     ALERT_2 = digitalRead(ALARM_2);
     ALERT_3 = digitalRead(ALARM_3);
     ALERT_4 = digitalRead(ALARM_4);

     if (ALERT_1 || ALERT_2 || ALERT_3 || ALERT_4) {
          alarmnow = 1;
     }

     if (Serial.available()) {
          alarmcheck = Serial.read();
     }

     switch (alarmcheck) {
          case '1':
               if (ALERT_1) {
                    Serial.println("1:1");
               } else {
                    Serial.println("1:0");
               }
               break;
          case '2':
               if (ALERT_2) {
                    Serial.println("2:1");
               } else {
                    Serial.println("2:0");
               }
               break;
          case '3':
               if (ALERT_3) {
                    Serial.println("3:1");
               } else {
                    Serial.println("3:0");
               }
               break;
          case '4':
               if (ALERT_4) {
                    Serial.println("4:1");
               } else {
                    Serial.println("4:0");
               }
               break;
          case ' ':
               break;
          default:
               Serial.println("X:1");
               break;
     }

     if (alarmnow) {
          BlinkLED();
     } else if (ledon) {
          digitalWrite(ledPin, LOW);
     }
}

void BlinkLED() {
     if (sinceAlarm >= 1000) {
          sinceAlarm = sinceAlarm - 1000;
          if (ledon) {
               ledon = 0;
               digitalWrite(ledPin, LOW);
          } else {
               ledon = 1;
               digitalWrite(ledPin, HIGH);
          }
     }
}

Once the firmware is loaded onto the teensy and all the switches are in place the linux system that will interface with the alarm needs to have a udev rule created that will allow the usbSerial interface to function.
Linux UDEV rules
/etc/udev/rules/49-teensy.rules

SUBSYSTEMS==”usb”, ATTRS{idVendor}==”16c0″, ATTRS{idProduct}==”04[789]?”, MODE:=”0666″ KERNEL==”ttyACM*”, ATTRS{idVendor}==”16c0″, ATTRS{idProduct}==”04[789]?”, SYMLINK+=”ttyUSB00%n”, MODE:=”0666″, ENV{ID_MM_DEVICE_IGNORE}=”1″

The host that connects to the ZZ-Teensy-Alarm needs to be able to query to the teensy on the USB Serial device to determine the status of any of the configured alarm switch inputs.  This is accomplished using a C program that will open the USB serial device presented by the teensy and write/read to the running firmware queries on port status.

The alarm-monitor application is a very simple C application. After initializing some variables it performs a quick check on the number of command line arguments, providing help and exiting if it is not correct. Next the application confirms that the query provided on the command line is a valid interface to query. Alarm-monitor then initializes the specified serial device to 38400 8n1 and writes out the query to the teensy serial device. Once the query is written the application will wait for a response for 10 seconds after which the appropriate response is sent back to the user.
Linux Command line zz-teensy-alarm query:
alarm-monitor.c

#include
#include
#include
#include
#include
#include
#include 

int main(int argc,char** argv)
{
        struct termios tio;
        struct termios stdio;
        time_t start,now;
        int diff;
        int tty_fd;
        fd_set rdset;
        struct flock fl;

        unsigned char c=' ';

        if (argc0) {
            write(STDOUT_FILENO,&c,1);
          }
          now = time(NULL);
          diff = (int)difftime(now,start);
        }

        fl.l_type   = F_UNLCK;
        fcntl(tty_fd, F_SETLK, &fl);
        close(tty_fd);

        if (diff >= 10) {
          printf("X:X\n");
          exit(1);
        }
        exit(0);
}

The alarm_monitor application can be compiled with gcc:
gcc -o alarm_monitor alarm_monitor.c

alarm_monitor has 2 inputs, the first is the USB device of the ZZ-Teensy-Alarm, the 2nd is the port to be queried (1-4 is hard coded, any additional ports need to be expanded on for alarm_monitor.c and zz_alarm0.ino).

EX:
alarm_monitor /dev/ttyUSB000 1
1:0

Would query alarm switch 1 and as this example shows returns the alarm #:status where 0 is OK and 1 is switch open (alarm).

There is a 10 second timeout if ZZ-Teensy-Alarm device isn’t connected or if there are connectivity issues.  An error code of X:X is returned for any timeout and any query to ports other than 1,2,3,4 return invalid query.

Concept Assembly

The initial design was built using a breadboard and is pictured below:

ZZ Servers Teensy Cabinet Alarm Prototype

Once the design was tested a standard radio shack project box was acquired along with a few screw down termination jacks.  These were assembled into the following picture

ZZ Cabinet Alarm Prototype – Assembly

ZZ Cabinet Alarm Prototype – Assembly

The final configuration has the usb cable coming out one side, an led on one side and the screw on terminators ready to be connected to magnetic door switches.

ZZ Cabinet Alarm Prototype – Assembled

ZZ Cabinet Alarm Prototype – LED on

Zabbix Integration

Once the alarm is in place it needs to be monitored. Here at ZZ Servers we leverage Zabbix but any system such as Nagios could work as long as they can execute a script for input.

Zabbix monitoring can monitor the status of each door alarm through the configuration of UserParameters.  A full configuration will follow in a future post with templates for items / alerts but for now below is a sample UserParameter for each of the 4 configured alarm monitors:

/etc/zabbix/zabbix_agentd.conf

UnsafeUserParameters=1
UserParameter=cabinetdoor[*],/usr/local/bin/alarm-monitor /dev/ttyUSB000 $1|cut -d: -f 2

Details on how to configure the zabbix template including the appropriate items, triggers and alerts will be posted in my next blog post.

UPDATE: 1/30/2012 – Video of Shmoocon Firetalk – http://vimeo.com/35933398

About The Author

Scroll to Top