↓ Archives ↓

Category → automation

Capturing the Screen or Video of Your Virtual Machines Using X, Vnc, Rdp or Native ways

With this blogpost we continue the previous investigation to interact with a virtual machine using X, VNC , RDP or native ways with a virtual machine. This time instead of sending keystrokes we are looking for capture screenshots or even capture the complete session as a video.

Interacting with X-Windows

Capturing a screenshot:

Grabbing a screen of an X-Windows session is easy: in order to grab the screen on an X-session on :1 issue the following command:

$ import -window root -display :1 screenshot.png

Recording a video:

I got the idea of using the ffmpeg command for capturing the X-session from

http://gasubasu.com/2009/05/06/flash2video/

So I installed ffmpeg using macports

$ sudo port install ffmpeg

When I executed it, it complained about Unknown input format: 'x11grab'

$ ffmpeg -f x11grab -vc x264 -s vga -r 30 -b 2000k -g 300 -i :1.0 session-recording.avi
FFmpeg version 0.6, Copyright (c) 2000-2010 the FFmpeg developers
  built on Aug 29 2010 16:56:51 with gcc 4.2.1 (Apple Inc. build 5664)
  configuration: --prefix=/opt/local --enable-gpl --enable-postproc --enable-swscale --enable-avfilter --enable-avfilter-lavf --enable-libmp3lame --enable-libvorbis --enable-libtheora --enable-libdirac --enable-libschroedinger --enable-libfaac --enable-libfaad --enable-libxvid --enable-libx264 --enable-libvpx --enable-libspeex --enable-nonfree --mandir=/opt/local/share/man --enable-shared --enable-pthreads --disable-indevs --cc=/usr/bin/gcc-4.2 --arch=x86_64
  libavutil     50.15. 1 / 50.15. 1
  libavcodec    52.72. 2 / 52.72. 2
  libavformat   52.64. 2 / 52.64. 2
  libavdevice   52. 2. 0 / 52. 2. 0
  libavfilter    1.19. 0 /  1.19. 0
  libswscale     1.11. 0 /  1.11. 0
  libpostproc   51. 2. 0 / 51. 2. 0
Unknown input format: 'x11grab'

There was no variant or option for macports to have ffmpeg take this flag for compiling. So I downloaded the sourcecode from the ffmpeg repository:

$ wget http://www.ffmpeg.org/releases/ffmpeg-0.6.tar.gz
$ cd ffmpeg-0.6
$  ./configure --enable-x11grab --enable-gpl

Although I had specified the --enable-x11grab option, ffmpeg still complained about the Unknown input format.

I got some clues here and here. And after a look at the the configure file, I found that in order to work, it required the Xfixes and Xext libraries:

enabled x11grab                         &&
check_header X11/Xlib.h                 &&
check_header X11/extensions/XShm.h      &&
check_header X11/extensions/Xfixes.h    &&
check_func XOpenDisplay -lX11           &&
check_func XShmCreateImage -lX11 -lXext &&
check_func XFixesGetCursorImage -lX11 -lXext -lXfixes

So after installing those libraries:

$ sudo port install xorg-libXext
$ sudo port install xorg-libXfixes
$  ./configure --enable-x11grab --enable-gpl --enable-nonfree --extra-cflags="-I/opt/local/include -I/opt/local/include/X11"  --extra-ldflags=-L/opt/local/lib

....
Enabled indevs:
x11_grab_device
...

Now with the input x11_grab_device available, I was all set to record a running session.

$ ./ffmpeg -f x11grab -vc x264  -s xga -r 30 -b 2000k -g 300 -i :1.0 session-recording.avi
FFmpeg version 0.6, Copyright (c) 2000-2010 the FFmpeg developers
  built on Aug 30 2010 09:11:02 with gcc 4.2.1 (Apple Inc. build 5664)
  configuration: --enable-x11grab --enable-gpl --enable-nonfree --extra-cflags='-I/opt/local/include -I/opt/local/include/X11' --extra-ldflags=-L/opt/local/lib --disable-shared
  libavutil     50.15. 1 / 50.15. 1
  libavcodec    52.72. 2 / 52.72. 2
  libavformat   52.64. 2 / 52.64. 2
  libavdevice   52. 2. 0 / 52. 2. 0
  libswscale     0.11. 0 /  0.11. 0
[x11grab @ 0x10100b000]device: :1.0 -> display: :1.0 x: 0 y: 0 width: 1024 height: 768
[x11grab @ 0x10100b000]shared memory extension  found
[x11grab @ 0x10100b000]Estimating duration from bitrate, this may be inaccurate
Input #0, x11grab, from ':1.0':
  Duration: N/A, start: 1283153847.281641, bitrate: 754974 kb/s
    Stream #0.0: Video: rawvideo, bgra, 1024x768, 754974 kb/s, 30 tbr, 1000k tbn, 30 tbc
File 'session-recording.avi' already exists. Overwrite ? [y/N] y
Output #0, avi, to 'session-recording.avi':
  Metadata:
    ISFT            : Lavf52.64.2
    Stream #0.0: Video: mpeg4, yuv420p, 1024x768, q=2-31, 2000 kb/s, 30 tbn, 30 tbc
Stream mapping:
  Stream #0.0 -> #0.0
Press [q] to stop encoding
frame=  310 fps= 30 q=2.0 Lsize=    2810kB time=11.83 bitrate=1945.1kbits/s dup=0 drop=21    
video:2796kB audio:0kB global headers:0kB muxing overhead 0.502798%

This worked fine! But the recording only captured 1024x768 (flag = xga), when I tried to have ffmpeg capture a larger screen 1600x1024 (flag wsxga), it told me an error about Can't get shared memory . No clue why. For now, the xga option is enough for me.

./ffmpeg -f x11grab -vc x264  -s wsxga -r 30 -b 2000k -g 300 -i :1.0 session-recording.avi
FFmpeg version 0.6, Copyright (c) 2000-2010 the FFmpeg developers
  built on Aug 30 2010 09:11:02 with gcc 4.2.1 (Apple Inc. build 5664)
  configuration: --enable-x11grab --enable-gpl --enable-nonfree --extra-cflags='-I/opt/local/include -I/opt/local/include/X11' --extra-ldflags=-L/opt/local/lib --disable-shared
  libavutil     50.15. 1 / 50.15. 1
  libavcodec    52.72. 2 / 52.72. 2
  libavformat   52.64. 2 / 52.64. 2
  libavdevice   52. 2. 0 / 52. 2. 0
  libswscale     0.11. 0 /  0.11. 0
[x11grab @ 0x10100b000]device: :1.0 -> display: :1.0 x: 0 y: 0 width: 1600 height: 1024
[x11grab @ 0x10100b000]shared memory extension  found
[x11grab @ 0x10100b000]Fatal: Can't get shared memory!

Interacting with VNC

Capturing a screenshot:

I found two projects that touted the ability to capture the screen of a VNC session:

None of them really worked for me. So I resorted back to using Xvfb as an intermediate:

$ vncpasswd mypasswordfile
$ Xvfb  :1 -screen 0 1024x768x24 -fbdir /var/tmp/
(EE) AIGLX error: dlopen of /usr/X11/lib/dri/swrast_dri.so failed (dlopen(/usr/X11/lib/dri/swrast_dri.so, 5): image not found)
(EE) GLX: could not load software renderer
(EE) XKB: Couldn't open rules file /usr/X11/share/X11/xkb/rules/base
(EE) XKB: No components provided for device Virtual core keyboard
$ DISPLAY=:1 rdesktop -u username -p password -d domain remotehost
$ import -window root -display :1 screenshot-rdp.png

Recording a video:

To record an vnc session to flash I found the following pointers:

I went for the C version of vnc2swf :

$ wget http://www.unixuser.org/~euske/vnc2swf/vnc2swf-0.5.0.tar.gz
$ cd vnc2swf-0.5.0
$  LDFLAGS=-L/usr/X11R6/lib  ./configure 
$  LDFLAGS=-L/usr/X11R6/lib  make
$ ./vnc2swf
usage: ./vnc2swf [<options>] <out.swf> <host>:<display#>
       ./vnc2swf [<options>] <out.swf> -listen [<display#>]

<options> are standard Xt options, or:
              -shared
              -viewonly
              -fullscreen
              -passwd <passwd-file>
              -noauto
              -encodings <encoding-list> (e.g. "raw copyrect")
              -geometry <geometry>
              -bgr233
              -owncmap
              -truecolour
              -depth <depth>
              -framerate <frames-per-sec>
              -startrecording
              -nowindow
              -nostatus
              -clippinggeometry <geometry>

Àn alternative is to ffmpeg as explained in the X-session, with vncviewer displayed in Xvfb session


Interacting with RDP

Capturing a screenshot:

I found 0 tools to capture a screenshot directly from an RDP (at least a tool on Linux or MacosX). While poking around I found a reference to VNC inside the code of Rdesktop: there was mention of rdp2vnc

$ ./configure --help |grep vnc
  --with-libvncserver-config=CMD  use CMD as libvncserver-config
  --with-libvncserver     make rdp2vnc

I almost got it to work by checking out the latest svn

$ svn co https://rdesktop.svn.sourceforge.net/svnroot/rdesktop rdesktop
$ cd rdesktop

It required the installation of LibVNCServer library - http://libvncserver.sourceforge.net/

./configure --prefix /opt
make
make install
$ cd rdesktop
$ ./configure  --with-libvncserver  --x-includes=/opt/local/include/ --x-libraries=/opt/local/lib --with-libvncserver-config

But in the end - nothing and I was facing a Compile problem

So of to the X-session fallback using rdesktop and xvfb:

$ Xvfb  :1 -screen 0 1024x768x24 -fbdir /var/tmp/
(EE) AIGLX error: dlopen of /usr/X11/lib/dri/swrast_dri.so failed (dlopen(/usr/X11/lib/dri/swrast_dri.so, 5): image not found)
(EE) GLX: could not load software renderer
(EE) XKB: Couldn't open rules file /usr/X11/share/X11/xkb/rules/base
(EE) XKB: No components provided for device Virtual core keyboard
$ DISPLAY=:1 vncviewer -FullColor --Passwordfile mypasswordfile localhost
$ import -window root -display :1 screenshot-vnc.png

Recording a video:

The only way (I found) is to capture the RDP session as a video is to use ffmpeg as explained in the X-session, with rdesktop displayed in Xvfb session.


Virtual Server helpers:

Capturing screenshots with Vmware Esx

The Vsphere API allows the creation of a task by using CreateScreenshot_Task() command. I used the VMware VI (vSphere) Java API - http://vijava.sourceforge.net/ and coded an example to create the screenshot. The file itself is saved by vmware in the datastore root, so you need something like scp to grab it from there.

import java.net.URL;
import com.vmware.vim25.*;
import com.vmware.vim25.mo.*;

public class takescreenshot 
{
    public static void main(String[] args) throws Exception
    {
        String url="https://localhost/sdk"; 
        String username="root"; 
        String password="thepassword"
        long start = System.currentTimeMillis();
        ServiceInstance si = new ServiceInstance(new URL(url), username, password, true);
        long end = System.currentTimeMillis();
        System.out.println("time taken:" + (end-start));
        Folder rootFolder = si.getRootFolder();
        String name = rootFolder.getName();
        System.out.println("root:" + name);
        ManagedEntity[] mes = new InventoryNavigator(rootFolder).searchManagedEntities("VirtualMachine");
        if(mes==null || mes.length ==0)
        {
            return;
        }
        
        VirtualMachine vm = (VirtualMachine) mes[0]; 
        Task screentask=vm.CreateScreenshot_Task();
        si.getServerConnection().logout();
    }
}

For an example using the perl API have a look at the script at http://communities.vmware.com/docs/DOC-10497

Screencapture and recording a video in Virtualbox

I found that virtualbox itself has a way to capture the screen (--capture) and to record the session to file. Internally it uses ffmpeg. I wasn't able to run it on my mac, as it is not supported on that platform, but it might work for you.

$ VBoxVRDP --help
Oracle VM VirtualBox Headless Interface 3.2.8
(C) 2008-2010 Oracle Corporation
All rights reserved.

Usage:
   -s, -startvm, --startvm <name|uuid>   Start given VM (required argument)
   -v, -vrdp, --vrdp on|off|config       Enable (default) or disable the VRDP
                                         server or don't change the setting
   -p, -vrdpport, --vrdpport <ports>     Comma-separated list of ports the VRDP
                                         server can bind to. Use a dash between
                                         two port numbers to specify a range
   -a, -vrdpaddress, --vrdpaddress <ip>  Interface IP the VRDP will bind to 
   -c, -capture, --capture               Record the VM screen output to a file
   -w, --width                           Frame width when recording
   -h, --height                          Frame height when recording
   -r, --bitrate                         Recording bit rate when recording
   -f, --filename                        File name when recording.  The codec
                                         used will be chosen based on the
                                         file extension

Sending Keystrokes to Your (Virtual) Machines using X, Vnc , Rdp or Native ways

Options overview

The most common way to interact with a virtual machine is by remote login via ssh. This blogpost is about a different way of interaction: it will show you how to send keystrokes (or mouse) directly to the remote screen of the machine. This can be used for instance for kickstarting a machine before the network is up (typing linux), or automating things that require screen interaction.

In general three types of remote screen sessions exist:

  • VNC: is a graphical desktop sharing system that uses the RFB protocol
  • RDP: A proprietary developed by Microsoft
  • X-session: based on the X Window System (commonly X or X11)

Most virtualization solutions allow you to activate one of these remote session options:


Keycodes versus keys

Before we start of with the different solutions, I would like to point out that sending keystrokes to these remote sessions is not the same as printing a character to the screen.

Keyboards and for that matter virtual keyboards interact with scancodes : every key on the keyboard is assigned a scancode. This has a few consequences:

  • An uppercase letter is a combination of a scancode for Shift and a scancode for the letter
  • The same scancode on a different keyboard layout can cause a different letter to be send: f.i. scancode 10 (hex) generate a 'Q' on a Qwerty keyboard but 'A' on an Azerty keyboard

Most of the tools assume the use of a US Layout keyboard. A good overview of the different scancodes can be found at http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html


Interacting with X-Windows

Before VNC and RDP existed , people already had the option of using an X-client to an X-server.

P.S. I know you are probably looking for RDP or VNC interaction, but this description will come in handy as we will use this through Xvfb.

I found two tools that help sending keystrokes:

I couldn't get xte to compile on my mac but xdotool is conveniently available in macports.

What is xdotool? This tool lets you simulate keyboard input and mouse activity, move and resize windows, etc. It does this using X11's XTEST extension and other Xlib functions. Additionally, you can search for windows and move, resize, hide, and modify window properties like the title. If your window manager supports it, you can use xdotool to switch desktops, move windows between desktops, and change the number of desktops.

$ sudo port install xdotool
--->  Computing dependencies for xdotool
--->  Fetching xdotool
--->  Verifying checksum(s) for xdotool
--->  Extracting xdotool
--->  Applying patches to xdotool
--->  Configuring xdotool
--->  Building xdotool
--->  Staging xdotool into destroot
--->  Installing xdotool @2.20100818.3004_0
--->  Activating xdotool @2.20100818.3004_0

To use xdotool (and avoid the error message "Error: XTEST extension unavailable
on '(null)'") you need to enable the XTEST extension. If you're using Apple's
X11.app, the command to do so is: 

defaults write org.x.X11 enable_test_extensions -boolean true 

If you're using the MacPorts X11.app, use: 

defaults write org.macports.X11 enable_test_extensions -boolean true 

This only needs to be done once.
--->  Cleaning xdotool

Sending the key 'a' to your X-sessions

$ xdotool key 'a'

The tool has a lot of ways of interacting with the windows, and for our purposes it has a option to send a key and even mouse .

$ xdotool
Usage: xdotool <cmd> <args>
Available commands:
  getactivewindow
  getwindowfocus
  getwindowname
  getwindowpid
  search
  selectwindow
  help
  version
  click
  getmouselocation
  key
  keydown
  keyup
  mousedown
  mousemove
  mousemove_relative
  mouseup
  type
  windowactivate
  windowfocus
  windowmap
  windowmove
  windowraise
  windowsize
  windowunmap
  windowreparent
  windowkill
  set_window
  behave
  set_num_desktops
  get_num_desktops
  set_desktop
  get_desktop
  set_desktop_for_window
  get_desktop_for_window
  get_desktop_viewport
  set_desktop_viewport

Interacting with VNC

Using Ruby-vnc

The easiest way I found to interact with a VNC session is by using the excellent ruby-vnc library http://code.google.com/p/ruby-vnc/

The example on the website gives you a good idea on how it works:

# launch xclock on localhost. note that there is an xterm in the top-left
Net::VNC.open 'localhost:0', :shared => true, :password => 'mypass' do |vnc|
  vnc.pointer_move 10, 10
  vnc.type 'xclock'
  vnc.key_press :return
end

Using vncviewer and xvfb

If you are not that confident with ruby, there is another option. Xvfb allows you to create a virtual screen on which you can interact. Together with the vncviewer (X-client) we can use it with xdotool to interact with a vnc session.

The first step is to create a vnc password file, as with vncviewer the password can not be supplied on the commandline.

Create password file name 'mypasswordfile'

$ vncpasswd mypasswordfile
Password:
Verify:

Start Xfvb screen (:1 means another display and 24 is the colordepth)

$ Xvfb  :1 -screen 0 1024x768x24 -fbdir /var/tmp/
(EE) AIGLX error: dlopen of /usr/X11/lib/dri/swrast_dri.so failed (dlopen(/usr/X11/lib/dri/swrast_dri.so, 5): image not found)
(EE) GLX: could not load software renderer
(EE) XKB: Couldn't open rules file /usr/X11/share/X11/xkb/rules/base
(EE) XKB: No components provided for device Virtual core keyboard

Now we can start vncviewer (that logs in automatically) within the Virtual Frame buffer

$ DISPLAY=:1 vncviewer -FullColor --Passwordfile mypasswordfile localhost

Now we are back to using xdotool to interact with the session.


Interacting with RDP

Using properjavardp

Properjava rdp - http://properjavardp.sourceforge.net/ is a full implementation of the RDP protocol in Java. It doesn't seem to be maintained and several 'forks' are available.

I used the original source to get it running:

  • Import the src directory in a java project
  • Add the src files like RdpPacket_Localised for the src-1.4 directory
  • Add the jar files to the project (log4j.jar, ..)

The following code will give you an idea on how you can use it

package net.propero.rdp;

import java.net.InetAddress;
import net.propero.rdp.rdp5.Rdp5;
import net.propero.rdp.rdp5.VChannels;

public class SendKeysRdp {
    public static void main(String[] args) {
        int logonflags = Rdp.RDP_LOGON_NORMAL;
        Rdp5 RdpLayer = null;
    
        VChannels channels = new VChannels();
        RdpLayer = new Rdp5(channels);
        Common.rdp = RdpLayer;      
        try {
/*
 *                  RdpLayer.connect(Options.username, InetAddress
                            .getByName(server), logonflags, Options.domain,
                            Options.password, Options.command,
                            Options.directory);         
 */
            RdpLayer.connect("Username", InetAddress.getByName("192.168.2.30") , logonflags, "Domain","Command", "","Directory");
            RdpLayer.sendInput(Input.getTime(), Input.RDP_INPUT_SCANCODE, Input.RDP_KEYPRESS, 0x1f, 0);

        } catch(Exception e){
            System.out.println(e.toString());
        }
    }
}

Using rdesktop and Xvfb

$ sudo port install rdesktop
$ Xvfb  :1 -screen 0 1024x768x24 -fbdir /var/tmp/
(EE) AIGLX error: dlopen of /usr/X11/lib/dri/swrast_dri.so failed (dlopen(/usr/X11/lib/dri/swrast_dri.so, 5): image not found)
(EE) GLX: could not load software renderer
(EE) XKB: Couldn't open rules file /usr/X11/share/X11/xkb/rules/base
(EE) XKB: No components provided for device Virtual core keyboard
$ DISPLAY=:1 rdesktop -u <username> -p <password> -d <domain> remotehost

Directly from C code re-using code from rdesktop

I found a link to an xrdp overflow tool that seems to use the rdp_send_scancode function to send the keystroke.

http://webcache.googleusercontent.com/search?q=cache:GIucABEgkT8J:packetstormsecurity.org/0904-exploits/xrdp-overflow.txt+rdp_send_scancode&cd=7&hl=nl&ct=clnk&gl=be&client=firefox-a

Another cool trick I picked up, was the way to launch commands using rdesktop :

http://www.singularity.be/2008/03/using-rdesktop-to-script-windows.html


Virtualbox option

Virtualbox provides a way to send keyboard scancodes directly using it's excellent API. The option keyboardputscancode allows to specify the hex code of the scancode. You can also send multiple keycodes after each other. In my experience this doesn't work well for a long sequence. It seems the buffer it limited and your best option is to send the different keycodes in multiple calls to the command line.

VBoxManage controlvm        <uuid>|<name>
                            pause|resume|reset|poweroff|savestate|
                            acpipowerbutton|acpisleepbutton|
                            keyboardputscancode <hex> [<hex> ...]|

Using a non-headless solution

In order to automate visual tasks, there exist a lot of macro/recording tools, the downside of them is that they need an actual display to run. When scripting you are mostly looking for headless solutions though. The disadvantage of headless solutions is that they are cumbersome to create as you have to move the pointer and the keys at the exact spot.

Sikuli

When you are interacting with a session you as a person do a lot more then just typing things. You yourself also locate which window to focus, what button to click.

Sikuli is a great tool that tries to help you in those tasks. It uses image recognition tools to find the correct place on the screen. Think of it a visual scripting language for screen interaction. An example script looks like this:

It allows you use these commands within java and it seems that someone is working on replacing the java.awt.robot to a vnc version

More background can be found at:

The function type() simulates keyboard typing just as a user types text in a application. However, type() doesn't work for different keyboard layouts other than QWERTY, such as DVORAK. We provide a workaround paste() since 0.9.7 (20100127). The function paste() transfers text through system's clipboard, which is fully independent of keyboard layouts. A sample usage that paste "network" into a search box is shown as follows.

Other options

For completeness I provide different options I found useful

$ osascript -e 'tell application "System Events" to keystroke "LOGIN_NAME"'; \
$ osascript -e 'tell application "System Events" to keystroke tab'; \
$ osascript -e 'tell application "System Events" to delay 0.5'; \
$ osascript -e 'tell application "System Events" to keystroke "PASSWORDHERE"'; \
$ osascript -e 'tell application "System Events" to delay 0.5'; \
$ osascript -e 'tell application "System Events" to keystroke return'

Replace LOGIN_NAME and PASSWORD with the proper values
Run as "root" user

ssh tricks – the usual and beyond

SSH is an amazing beast. I nearly use it everyday and I'm amazed every time I learn something new. The following is a list of my tricks in the bag. It starts with the usual tricks that you find all over the place, but I hope there will be some new tricks for you too.

What's your best trick? Share it in the comments with the world. Nobody can know enough of ssh!

The basics:

Password-less login:

This is usually the first thing start doing when want automation with ssh

#Create a new keypair
$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/Users/patrick/.ssh/id_dsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/patrick/.ssh/id_dsa.
Your public key has been saved in /Users/patrick/.ssh/id_dsa.pub.
The key fingerprint is:
87:66:b7:a0:f6:0e:6a:71:2c:5d:ee:5f:17:2a:b7:2f patrick@localhost
The key's randomart image is:
+--[ DSA 1024]----+
|                 |
|                 |
|                 |
|        ..       |
|     o oS o   .  |
|    o ++.+ . . . |
|     ++.  o + .  |
|    .o o.  +Eo   |
|   ..  .o.. .o.  |
+-----------------+
$ cat ~/.ssh/id_dsa.pub | ssh user@remotehost "cat - >> ~/.ssh/authorized_keys"
$ ssh user@remotehost

Install your keys on a remote server:

$ ssh-copy-id -i ~/.ssh/id_dsa.pub user@remotehost
#Alternative 
$ cat ~/.ssh/id_dsa.pub | ssh user@remotehost "cat - >> ~/.ssh/authorized_keys"

Passphrase automation:

If you have protected your keys with a passphrase (which you should), then it is annoying to re-enter that all the time. You can avoid that by running your environment inside an ssh-agent and using ssh-add to enter the passphrase once.

$ ssh-add ~/.ssh/id_dsa
Need passphrase for /home/mah/.ssh/id_dsa (you@example.com).
Enter passphrase:
$

Pseudo Terminal :

some commands like sudo require a pseudo terminal to be activated

$ ssh -t patrick@remotehost sudo cat /etc/passwd

Avoid lastlog:

Log in without appearing in lastlog/w and who output.

$ ssh -T user@hostname.com

Piping

Example of using piping to backup over the network

$ ufsdump 0uf - /dev/md/rdsk/d33 | ssh r280n "dd obs=32k ibs=32k of=/dev/rmt/0n"

Rsync over ssh

$ rsync -avz -e "ssh -i /home/thisuser/cron/thishost-rsync-key" remoteuser@remotehost:/remote/dir /this/dir/ 

Tunnels and firewall-piercings:

X-forwarding:

$ ssh -X patrick@remotehost
Warning: untrusted X11 forwarding setup failed: xauth key data not generated
Warning: No xauth data; using fake authentication data for X11 forwarding.
Last login: Fri Aug 27 20:27:40 2010

Port forwarding:

Set up a localforward from the remote machine port 25 to a local port 9025

$ ssh -L 9025:localhost:25 patrick@remotehost

No command:

Sometimes you just want to setup a forward with having a shell

$ ssh -N -L 9025:localhost:25 patrick@remotehost

KeepAlive:

Getting tired of those timeouts by the firewall? Have ssh send a keepalive/

Put the following options in your $HOME/.ssh/ssh_config

    KeepAlive yes
    ServerAliveInterval 60

Socks Daemon for proxying: (-D)

Sometimes it's interesting to start a socks daemon. You can configure this in your browser to surf as it seems to come from the remote machine.

$ ssh -D 9999 patrick@remotehost

Tunneling over an http proxy:

Corporate firewalls often only allow http to go outside. See corkscrew

ProxyCommand /usr/bin/corkscrew proxy-ip 8080 %h %p ~/.ssh/myauth

Chaining ssh hopping:

Host pc1.example.org pc2.example.org
ForwardAgent yes
ProxyCommand ssh -qax bastion.example.org /usr/bin/nc -w 120 %h %p

Netcat mode:

Starting from openssh 5.4: we can have ssh act as netcat. (-W) This connects stdio on the client to a single port forward on the server. This allows, for example, using ssh as a ProxyCommand to route connections via intermediate servers.”

$ ssh -p 443 -W remotehost2:23 patrick@remotehost
Trying remotehost2...
Connected to remotehost2.
Escape character is '^]'.

User Name : ^]
telnet> close
$

Mounting over ssh:

Sometimes it's nice to mount a remote directory over ssh. Fuse and sshfs are your friend

$ sshfs remote-user@remote.server:/remote/directory /mnt/remote-fs/

http://fuse.sourceforge.net/sshfs.html

VPN Tunneling:

Did you know that ssh can do layer 2 and 3 VPN tunneling?

Check out ssh -w. Example from manpage:

$ ssh -f -w 0:1 192.168.1.15 true
$ ifconfig tun0 10.0.50.1 10.0.99.1 netmask 255.255.255.252

SSH http multiplexer:

sslh lets one accept both HTTPS and SSH connections on the same port. It makes it possible to connect to an SSH server on port 443 (e.g. from inside a corporate firewall) while still serving HTTPS on that port. http://www.rutschle.net/tech/sslh.shtml


Speed

Compression

If you are working on a slow link, compression (-C) and using a simple cipher (-c blowfish) saves you speed

$ ssh -C -c blowfish patrick@remotehost

Multiplexing - ControlMaster:

Another great way to speed up ssh is to re-use the same connection when you connect multiple times to the same host

$ mkdir –p ~/.ssh/connections
$ chmod 700 ~/.ssh/connections

Add this to your ~/.ssh/config file:
Host *
ControlMaster auto
ControlPath ~/.ssh/connections/%r_%h_%p

Managing keys

Ignore Hostkeys:

When you're re-installing a machine over and over again, you often want to get rid of the hostfile key verification. This is what you need:

$ ssh user@host -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null

Check if hostkey exists:

k$ ssh-keygen -F 192.168.2.152
# Host 192.168.2.152 found: line 31 type RSA
192.168.2.152 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwHH15HpeJo21wyqpe2iFM8/0CtoYnE9DDXfCewws7iMhM+vgp7pjnaC83IgAt7G/x/VDHcbnyuI4odrGSEAE5wm7LNuT6uSfQMbXCayE+uoOIrAVhf41ZnAFQrs/+Mutk5LFEjPPNhuriq5ltBT4UwMlYQMa5z/SzmxV0ZAGXks5GMDz0o89yUwRarRfsGudASEtzUxgnxnOo5STBMZOdQ0GNEVdfJDgfJDAOi34T1FidpCqAtm8akYuB+Qsj3/hDQmIT+GsKYaGNZvz8ZNnPBAc9kWlS6VqXXNreyEeu7AmHDWXjMP3NW1tsibmZ8zeOSZdmEVEiuaYCIvERDq3MQ==

Remove a hostkey:

$ ssh-keygen -R 192.168.2.152
/Users/patrick/.ssh/known_hosts updated.
Original contents retained as /Users/patrick/.ssh/known_hosts.old

Get hostkey of remote server:

$ ssh-keyscan remotehost
# remotehost SSH-2.0-OpenSSH_5.2
remotehost ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAyREFGMBB6Qi1uoEYIk4GlqLXdS26moAxmV69UX0icQjp0Rw53xZ/2L0ZQwhsUiFV1vq4QfZNeUO142IzBgSspgsJZ7wJq213tsE7WIJGIBqvWnhU3vJuL9wgYT8f6BAvLoEfapFhLy24TDmn2DXldJAYgo8MnUbRrJlvnhQZPpd5cDWCXkzPGQE8r7REZsAWbWNlVOFRvZioPoGCGYMtsDWSBelBISGkedoNpTSpRkMmBAnsHBfvIzDPoTDYL4PZR0jJ8MaJrDhRtD4caRw4HVyhzSa3/FCpcm09PyBRabH/CyxNSOZjLc2+N9Ph9AKeTNgvmxP70wx668XaGYwCrQ==

SSH DNS Keys

Instead of using your local hostfile, you can store your keys in DNS. Have a look at sshfp to do the job. Then you can specify that ssh needs to

$ ssh localhost -o "VerifyHostKeyDNS=yes"
yes authenticity of host 'localhost (127.0.0.1)' can't be established.
RSA key fingerprint is 2d:d3:29:bd:4d:e2:7d:a3:b0:15:96:26:d4:60:13:34.
Matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)?

SSH Escape Sequences:

It often happens to me that I'm working into an ssh shell that used forwarding. I always thought there was no way to change the forwarding rules and that I had to logout. It seems not! SSh has an internal shell activated by a tilde. Seeing is believing!

Escape sequences are only recognized after a newline and are initiated with a tilde (~) unless you modify it with the -e flag.

Hit ENTER ~? on a running ssh session to see a list of escapes:

Supported escape sequences:

~. – terminate connection
~B – send a BREAK to the remote system
~C – open a command line
~R – Request rekey (SSH protocol 2 only)
~^Z – suspend ssh
~# – list forwarded connections
~& – background ssh (when waiting for connections to terminate)
~? – this message
~~ – send the escape character by typing it twice
(Note that escapes are only recognized immediately after newline.)
~. and ~# are particularly useful.

Visualize hostkeys:

Every host key has it's own visual fingerprint

$ ssh -o VisualHostKey=yes patrick@localhost
Host key fingerprint is 9f:a0:03:c1:63:8b:b8:c6:d6:83:cb:22:33:cb:83:cc
+--[ RSA 2048]----+
|                 |
|   .             |
|    =            |
| . o +           |
|. . o   S        |
|..o  . . o .     |
|== o  o   o      |
|@E. .  .         |
|+B.              |
+-----------------+

Security hacks

Local Password sniffing:

If you have process that connects to your ssh and you want to see the password it's using, then strace is your friend.

$ ps axuww | egrep 'PID|ssh'
#Now become root and attach to the running daemon with strace, changing the PID as appropriate:

$ sudo strace -f -e 'read,write' -p12345

Remote Password sniffing:

A more passive way of listening into ssh sessions (v1) is using dsniff - Dsniff

Fingerprint fuzzing:

This one is to lure a lazy administrator into accepting your certificate. It generates keys with an almost similar fingerprint. http://freeworld.thc.org/papers/ffp.html

SSH Honeypot:

And to go totally security. Launch your own ssh honeypot and capture all the remote commands (and typos) with Kippo


Need more?

Top 50 SSH Helper tools - OMG!


Share yours! I'm definitely interested

Building Virtual Appliances

Johan from Sizing Servers asked me if I could talk about my experiences on building (virtual) appliances at their Advanced Virtualization and Hybrid Cloud seminar . Off course I said yes ..

Slides are below ... Enjoy ..

Trackback URL for this post:

http://www.krisbuytaert.be/blog/trackback/1005

Xpdays Benelux 2009 – Continuous Integration for the World

Here are the slides of my presentations at Xpdays Benelux 2009. The presentation is on how we could bring sysadmins and developers closer together by using continuous integration in both worlds.

I would very much like to thank Gildas Le Nadan for helping with the first version of it at Xpdays France 2009. Also I want to thank the organizers for this great conference! And for giving me the chance to speak about this rather niche subject.

Coding an Infrastructure Test First

Now that we outlined the programming languages for automating shell scripting, virtual machine creation ,network provisioning and os installation and beyond, I bet you as a devops are eager start writing your infrastructure code.

After some time chances are that you will end up with lots and lots of scripts executing in sequence. And then when you change something in a script the whole sequence will fail and you'll have a hard time looking for what caused the problem. A better approach for writing your code is to practice Test Driven Development.

Test Driven Development Automation

In short before writing any code, you first write a test for the code you are writing. Then you run your tests and see that the new test fails.(RED). It is only then that you start writing your code or change your existing code (REFACTOR). When you think the code is done, you run your tests again and see them succeed (GREEN). And then you continue to use this cycle to grow your code. It is important that you chance your code in small increments. For more infor see Test first guidelines.

Benefits for the sysadmin

So how can this help you as a sysadmin? Isn't this more of developer thing? And the answers is a big NO:

  • can you remember the last time when you had to apply patches or config file changes to a system. And did you have that fingers crossed feeling? Wouldn't it be great that you could install a patch and run a series of tests to see if everything behaved the way it should?
  • when you get audited : how can you show that the machines you're running comply to your installation guides
  • writing these tests also helps in sharing the knowledge and repeating the validation process every time. Even without your rockstar sysadmin being around
  • the incremental approach also helps in systems overdesign. As project complexity grows, you may notice that writing automated tests gets harder to do. This is your early warning system of overcomplicated design. Simplify the design until tests become easy to write again, and maintain this simplicity over the course of the project.

Yes, but won't this slow me down? Well this is exactly the reaction most developers have to this process. But to me the benefits should be clear, do you rather go for the fingers crossed go live or the verified state. You will have to find the right balance between writing all tests and writing no tests.

If you are working on a new project that tries to get something running as quickly as possible as a one shot, you'll probably be under pressure to deliver. It will take some time to reach the skills to get the automation and the tests in your fingers. Still a good way of convincing management is by explaining them that these tests not only for the project phase but can be used during the whole maintenance period. This definitely increased the ROI of writing these tests.

Setup / Teardown

Part of a test there usually is a setup and a tear-down part. The idea is to create a state under which you start performing your tests. For applications f.i. this would be to put the right stuff in the database, set the right variables. So how does this translate to machines? Most of the virtualization solutions allow you to do easily take snapshots of both your memory state (savestate) and your disks. So you can easily recreate a certain point in time to start your tests by cloning your systems and running some scripts to change the state (f.i. filling a disk, killing a process) . Another approach could be to take snapshots of your disks using filesystems like ZFS, LVM that easily let you take snapshots.

This also helps during the coding of your infrastructure:

  • you take a snapshot of your current state
  • you run your code
  • see if the test succeeds
  • if not OK rollback , if OK save the new state

Example: a Webserver Test First

In the following example I will describe the setup of a webserver which serves static pages. Note that there is no standard development involved here. It's all about pre-packaged software.

Step 1: Defining the virtual machine

In this step you would define the hardware of your virtual machine: number of CPU's, memory, network interfaces, mac addresses, disks, ... So how do we test this? In the days of physical hardware, the way to verify that systems had all the hardware in it, we booted up a CD and verified using commands that the system contained the correct number of disks.

To avoid writing your own boot CD , you could use something similar to sysrescueCD. It has a feature called autorun that allows you to boot up the disk and execute scripts that are either on a floppy, disk, NFS or Samba share or HTTP server. If you mount this as a virtual CD in your virtual machine, it can boot up the virtual machine and execute test to test if the definition was according to what was specified.

Step 2: Prepare IP, DNS, DHCP, TFTP

Next step is provisioning the network information for your virtual machine. In order to test this, we can easily use the same boot CD approach to verify this via scripts using dhclient, dig, tftpclient. Some might argue that this test is better done from within the OS. Off course testing is when the OS is installed is a more complete tests. Doing this test separate from the installed OS, lets you better distinguish where the error occurs. Is it a problem of the OS driver that there is no IP address or is is a problem with the definition of the DHCP/DNS.

Step 3: Minimal Install of OS

This is usually done by defining a kickstart/jumpstart template. It would contain the disks partitioning, network configuration, the minimal packages and patches, a set of minimal services enabled (ssh, puppet), selinux enabled , . As you see there is a lot more that can be tested here:

  • Is the swap activated correctly
  • Is all memory seen from the OS
  • check if disks partitioning is ok
  • check if disks are mounted correctly
  • Is it 64/32 bit
  • Are permission set right
  • Is SElinux activated
  • is the NFS share exported: showmount -e
  • IP: are the interfaces up an did they get the correct settings
  • do some DNS lookups to see if that works
  • Ping the router to see if network is alive
  • Verify the syntax of your sendmail.cf
  • See if the processes are running (sshd, puppetd) : ps -ef |
  • Check the listeners : netstat -an|grep LISTEN
  • Do a test login with SSH
  • Running NMAP to see if no other services are activated
  • run nessus to check vulnerabilities

Up until now these tests are simple checks , and probably most people will do similar things in their monitoring. I've talked about testing being more then monitoring. Aside from these simple checks you can complementing your monitoring to run scenarios. Also destructive tests like failing a disk or bringing down an interface are probably not the best thing to do in production monitoring ;-)

  • test if IP Bonding by executing a failover
  • Verify that syslog works by sending a log request
  • test if your raid system works by killing a disk
  • test if your self healing works by killing a process
  • test a reboot scenario
  • test if your DNS failover works by using iptables to block access to the first DNS server
  • test your backup/restore scenario ;-)

So aren't we re-testing the packages here? The problem here is packages are often tested in isolation from each other and they can only test a limited number of setups. That means it still makes sense to repeat some tests so see if YOUR combination of things actually works.

Step 4: Apply recipes for the webserver

We now have a tested minimal OS running with a configuration mgt system active. Next in line it applying recipes. While discussing these with a number of people , I heard a lot that you don't need tests because these tools work in a declarative mode: if something doesn't work then it's either an error in your recipe or an error in the configuration management software.

I personally disagree, the argument is similar to why we test even if individual packages are tested: you can write a beautiful recipe to install a webserver but maybe the firewall is preventing you access, maybe your SELinux is blocking things. Or you install a bad apache config file so that it uses the wrong directory to serve.

Also you can add scenario testing here :

  • by running load and see if it actually spawns the number of processes you specified
  • check for loadbalancer pages are available
  • kill the http daemon and see if it recovers
  • check if caching works ok by downloading the file one
  • check if HTTP/compression works
  • check if lastmodified/ headers are set correctly
  • check if log rotation works ok

Testing Frameworks

Programming languages have developed a lot of frameworks over time to help in writing tests. When coding your infrastructure in these languages check what's available. Still there is no test library that is specific to systems testing. The closest are test frameworks for HTTP testing.

Currently this is an emerging field. There are already a lot of examples in the wild. As we adopt more the idea of testable infrastructure, these frameworks will emerge. The most notable is cucumber-nagios written by Lindsay Holmwood. It brings http testing closer to monitoring and into the sysadmin world. Now you can reuse your tests written in cucumber in your monitoring environment.

After a discussion at devopsdays 09 Lindsay is starting on a similar thing that allows to integrated ssh scripting in this framework, or what he calls Behavior driven infrastructure through cucumber And recently he announced on the agile system administration mailing list that he's joining forces with Adam Jacob of Opscode. So definitely more to come!