Linux Internet Kiosk using Debian-Live HOWTO

(Formerly: Debian Internet Kiosk with Firefox 2 HOWTO)

Version 0.03 - GFDL
James Barrett (

This is the revamped version of the Debian Internet Kiosk with Firefox 2 HOWTO. I have adapted it to use Iceweasel, which gives a major improvement for streamlining the process. I have gotten rid of the requirement to install from tarball. The extensions I use have matured a bit so as to give better functionality.

I have chosen to rename and completely overhaul the process. From now on I will be using the Debian-Live system to create Live-USB or Live-CD distributions, leaning towards the USB option. There are many pros and cons between CD and USB solutions, a of few which I will name right now.

Live-CD If an upgrade is ever needed, it will be painless (burn new CDs) Movable parts have a tendency to fail
Can be slower than USB or Hard Disk options
No SWAP space.
Live-USB Many USB flash module options to choose from (some models fit on the motherboard's USB pins)
Flash does not wear out from repeated reads
Some Flash modules are equipped with read-only switches to prevent tampering
No moving parts to wear out
Still slower than Hard Disks
No swap Space (DO NOT put swap on flash!)
Hard Disk
Can be set up to automatically upgrade
Swap space is available.
If the machine is unpatched and someone gets a shell, it is potentially toast

There is a fourth option which I have not yet used, and that is a mixture of a Live disk with a spinning disk drive. It seems viable, but if someone gets a shell they might be able to get root access and at least mangle the partition tables. The only option I can think of for Live HDD is an IDE Flash Module with a write-protect switch. Transcend sells such a thing, and it is what I used in my first prototype (although without using the switch...)

For the remainder of this HOWTO, we will only discuss the Live USB option, as that is what interests me the most. If done correctly the software should last at least as long as the hardware you install it onto. We ought to hope for a successful one-off construction (aka "set it and forget it") which we hope will require no upgrading. It will be a live distro, so any and all irregularities would be fixable through a quick reboot. I have chosen a USB flash drive that has a write-protect switch which will be flipped on only after writing the live system onto the module and testing it thoroughly. Modules have been made available which sit directly on top of USB header pins of a motherboard. These are internal components which would require someone to have access to the inside of the machine in order to mangle the data (although the machine ought to be inside of an enclosure anyway, right? :)

"What is Debian-Live and why should I care?"

Debian-Live is a collection of debian helper scripts which streamline the process of creating live debian system images on-the-fly. Most of the intricate procedures detailed in this HOWTO have been taken from the DebianLive Wiki at Reading through some of the pages on that site would not be a bad idea. There should be detailed instructions there about how to retrieve and install the latest debian-live packages.

Just to give a little bit of depth to this, here is a quick run-down of the process that we will be using:

$ mkdir ~/live-kiosk && cd ~/live-kiosk
$ lh_config --apt apt --apt-recommends disabled --binary-images usb-hdd \
  --debconf-frontend dialog --debconf-priority low --hostname kiosk \
  --interactive shell --bootloader syslinux --bootstrap-flavour minimal \
  --distribution etch ... ... ...
$ cp /usr/share/live-helper/hooks/mini ./config/chroot_local-hooks/mini
 {{ copy over any other hooks we need }}
$ nano -w config/chroot
 {{ remove "standard" from LH_PACKAGES_LISTS }}
$ cat >> config/chroot_local-packageslists/namedoesnotmatter << EOF
#below are optional
$ cd config/chroot_local-includes
 {{ copy over the pre-made includes that we need }}
$ cd ~/live-kiosk
$ su -
# cd /home/user/live-kiosk
# lh-build --force
 {{ stay put. when presented with a shell, check everything, twice! }}
# logout
$ qemu binary.img
 {{ test to your heart's content! If satisfied, continue...}}
$ su -
 {{ Plug in your USB key }}
# mv binary.img kiosk`date +%s`.img
# dd if=kioskNNNNNNNNNN.img of=/dev/your_usb_key_device_file

If you could not follow that, read on.


The image creation process requires:

Implementing a kiosk image requires at least:

Recommendations for implementation:

Getting Started: creating an includes tree

We will need to set up a tree of includes for our live system. The first part of this tree will be the $HOME includes, mainly one directory: "~/.mozilla" and one file: "~/.xinitrc". Optionally, ~/.xscreensaver and ~/ad-images/*.png can be added (might as well make the screens into billboards while they sit idle). It also will be required to create an /etc/init.d/kiosk script as well as /usr/local/bin/ script to facilitate booting the kiosk.

jim@mft:/home/user/live-helper/config/chroot_local-includes$ find ./
find: ./home/user/.mozilla: Permission denied

/home/user/.mozilla is pretty much what you would expect, I do not think I need to list all the files in that tree. Permissions will be addressed later on in the hooks file.

So, fire up a shell and let's get to creating this includes tree.

Create a user on your development machine. Name the user whatever you want but for brevity's sake we will name it "user". Login, start X, fire up Iceweasel.


In this section we will configure Iceweasel and xscreensaver, then choose what images we want to use as advertisements.


First let us get Iceweasel out of the way. Debian developers believe in free software. Iceweasel is really just a rebranding of Firefox to make it fit better into the Debian Free Software Guidelines. The functionality, engine, and UI are all the same. Extensions and all other add-ons work exactly the same way. The only difference is the name of the program and its markings.

Install and configure extensions: Part 1/2

Install "Stealther", "Restart Firefox", "NewTabURL" and "Public Fox"

These extensions bring some very needed security to Iceweasel. With Public Fox, the capabilities to stop downloads and prevent people from installing extensions becomes a reality. With Stealther, nothing gets cached, and therefore no personal information about what sites the kiosk users are visiting gets written to disk. NewTabURL will allow you to set what page a person is brought to when they close all tabs or open a new one. The Restart Firefox extension adds a nice little button that restarts firefox whenever it is clicked.

To install these, click tools, addons, and the extensions window should pop up. Click "Get Extensions" (bottom right corner of that dialog). Search for (separately) and install these four extensions:

Click the "restart firefox" button on the bottom-right of the addons dialog. After it restarts, go to tools, then add-ons.

Public Fox

Click the preferences button for Public Fox. Turn on blacklisting, Enable URL blocking, Block links to blacklisted sites and set a blacklist location value exactly to this:


The asterisks need to be exactly in those places. It will not deter someone from putting, say, file:///etc/passwd into the navbar, but it will deter them from browsing the filesystem all willy-nilly.
You want to check everything on the upper-left hand side (except for the one referring to bookmarks, as this has caused my kiosk to malfunction - securing bookmarks are addressed later in this howto). Type a Lock Password and set extensions that you dont want downloaded to a single asterisk


so that it will block all downloads. Click the OK button.


Click Stealther and click the preferences button. Do not hide from the tools menu (you do need to enable it, after all!) and do not show a statusbar icon. Remove anything that is in the Toggle Key box. Under "Disable these options when activated", everything should be checked except cookies! This will still allow people to check their webmail. Later on we will configure Firefox to clear any cookies whenever it restarts. Click the OK button. Remeber to go to the tools menu and enable stealther.

Restart Firefox

Click the "Restart Firefox" extension, and click the preferences button. Select whether you wish for a (disturbingly ugly) dialog window should pop up when someone clicks the restart firefox button. Click OK. Close the add-ons dialog window. Right-click the home button next to the navigation bar, then click customize. Drag-and-drop the Search bar into the Cutomize Toolbar dialog window. Drag-and-drop the Restart Firefox button to where that used to be. You might also want to add a "new tab" button in there as well, it is up to you!


Click the NewTabURL extension, then click the preferences button. You will be able to allow new tabs to load with the set home page, a blank page or the current page. Setting it to a blank page would cause it to act the same way as before. Setting it to current page might confuse some people. I would say that the home page is the safest bet. I would suggest that you do not enable loading urls from the clipboard.

Restart Iceweasel (I mean, restart it!)

Configure Iceweasel


A number of boolean expressions must be entered here to add some security and make the kiosk run efficiently:

ui.allow_platform_file_picker false
browser.cache.disk.enable false
browser.sessionstore.enabled false
rkiosk.navbar true

Also, you will want to set the default bookmarks file. Set it to somewhere that your user can not write to, like /root/bookmarks.html like so:

browser.bookmarks.file /root/bookmarks.html


Here we will just be using some common sense. We do not want any dialogs to show up, because they will look horrid without any window manager decorations.

Main: Homepage is your choice (of course). Do not show the downloads window. Save files to any folder, but do not let it ask where to save the files. Do not have it check to see if Firefox is the fedault browser on startup.

Tabs: new pages should open in a new tab. Uncheck both "Warn me" config values and check "Always show the tab bar" and "When I open a link in a new tab, switch to it immediately"

Content: Block popups! Load images automatically! Enable Javascript! Do not enable Java unless you want to go through the trouble of installing it and configuring it (and updating it whenever someone reports a new java vulnerability...)

Content -> Advanced Javascript Settings: (Click advanced to the right of javascript) Do not allow any of these to be enabled!

Privacy: Remember nothing. Accept cookies, but keep until you close iceweasel. Always clear private data, do not ask before clearing it.

Privacy -> Clear Private Data (settings): Enable all.

Security: Warn when sites try to install add-ons. Forgery sites are your choice. Don't let Iceweasel remember any passwords.

Security -> Security Warnings (button to the right of warning messages): Don't have it warn about anything.

Advanced -> Update: Do not have it automatically check for any updates. You will want to do that yourself, which will save you the trouble of fixing a mess which a broken extension update could cause. Also, those unsightly popups could confuse your patrons.

Install and configure extensions: Part 2/2

Install R-Kiosk and Auto Reset Browser

R-Kiosk (now in the public domain) adds functionality which is needed to lock down Firefox. Auto Reset Browser does just what it alludes to; resets firefox after a period of inactivity. Install them just like you installed the first set of extensions in the above section

Restart and...

Whoops! This is what the end-result will hopefully look like; this is the locked-down kiosk. We're not done yet however, so switch to another desktop (alt-fN where N is a number between 1 and 4), open a terminal and type:

$ killall -9 firefox-bin

Restart firefox with the following command:

$ iceweasel -safe-mode

You will want to select the top check box (disable all extensions) and click the button on the bottom-left (continue in safe mode). Go to tools -> addons and disable r-kiosk. Close Iceweasel, and start it up as normal (by not using -safe-mode). Go to tools -> addons (quickly!), type in your PublicFox password and select "Auto Reset Browser". You want it to force exit and spawn a new window. 300 seconds (5 minutes) is a perfect time, but depending on your situation, you may have to change it. DO NOT FORGET to re-enable R-Kiosk, then restart it to test it out.


To stop the minimize/maximize/close buttons from appearing in the top-right corner of the screen when R-Kiosk is installed later, add this line to the file userChrome.css in ~/.mozilla/{default}/chrome/userChrome.css

@namespace url("");
#nav-bar #window-controls { display: none !important }


We need to tell X how to run the show. Fire up your favorite text editor (currently emacs for me), create a buffer and put the following code inside:

# give a nice white background for when Firefox reloads
xsetroot -solid white &
# optionally, the above can be commented out and the one below
# can be uncommented to use an image for the background (only 
# if xli is installed)
#xli - onroot -quiet /home/user/ad-or-logo.png &

# Configure xscreensaver accordingly before putting this thing
# into production. Think advertisements!
xscreensaver -no-splash &

# perpetuality ensues...
while true ; do

Save the buffer as ~/xinitrc (WITHOUT a dot, you don't want it to execute next time you login) and chmod that file a+x


Create a directory named "advert-images" (or whatever your little heart desires)... You could either choose to create your very own ~/.xscreensaver or simply start xscreensaver-demo and do the following:

Display modes: only one screensaver, choose "Distort". Obviously you do not want to lock the screen, so uncheck that if it is checked! Click the advanced tab up top, and select "Choose Random Image" under "Image manipulation". Type "/home/user/advert-images/" for the path to your image directory. Quit!


I would not put way too many images in this folder, approximately 5MB of images would be just enough. Images promoting free software are really dandy!

jim@mft:/home/user/live-helper/config/chroot_local-includes/home/user$ ls -al advert-images/
total 3984
drwxr-xr-x 2 user user      48 2007-09-03 22:28 .
drwxr-xr-x 5 user user      56 2007-09-05 15:28 ..
-rw-r--r-- 1 user user   78677 2007-09-03 22:28 berylicon512dr2.png
-rw-r--r-- 1 user user 1455802 2007-09-03 22:28 Espiral-Debconf6_2048x1536.png
-rw-r--r-- 1 user user  653869 2007-09-03 22:28 gnu-linux-black-wallpaper.png
-rw-r--r-- 1 user user  762517 2007-09-03 22:28 gnu-linux-color-wallpaper.png
-rw-r--r-- 1 user user  920817 2007-09-03 22:28 gnu-linux-white-wallpaper.png
-rw-r--r-- 1 user user  188014 2007-09-03 22:28 unclesam_rc_bugs.png

The option exists for you to create an init script which will update these images at boot time. Have the live system fetch a tarball of all the images, untar it and place the images in the correct directory with the correct permissions. Obviously, the tarball's web location and filename would have to remain the same for the lifetime of the kiosks.


Fire up your favorite text editor (currently emacs for me), create a buffer and put the following code inside:

# Disable the magic sysrq keystrokes
echo 0 >/proc/sys/kernel/sysrq

# Disable some X features 
echo -e '\nSection "ServerFlags"' >> /etc/X11/xorg.conf
echo 'Option "DontVTSwitch" "true"' >> /etc/X11/xorg.conf
echo 'Option "DontZap" "true"' >> /etc/X11/xorg.conf
echo 'Option "DontZoom" "true"' >> /etc/X11/xorg.conf
echo 'EndSection' >> /etc/X11/xorg.conf

# Make certain a few things are immutable, so as to quel various attacks
chattr +i /home/user/.mozilla/firefox/*/localstore.*
chattr +i /home/user/.mozilla/firefox/*/extensions.ini

echo 'Putting the machine into Kiosk mode ...'
sleep 1s
/usr/local/bin/ &

exit 0

Line 3 is there to stop the magic sysrq keystrokes. Lines 6 through 10 will effectively put the kibosh on some X server features. Line 7 stops ctrl-alt-fN buttons from working (they typically switch to a virtual console window). Line 8 stops X from being killed through ctlr-alt-backspace. Line 9 stops zooming of the desktop. Lines 13 and 14 will stop those two files listed from being altered. Lines 16 and 17 are strictly informational. Line 18 starts the kiosk script outside of the init process. Line 20 exits the script gracefully.

Save the buffer as a file named "rc.local" and exit, then chmod the file a+x.


This file will be invoked by the previous file. It will spawn su, run xinit and then reboot if xinit should ever die. Name it whatever you want, it will become /usr/local/bin/

# this is /usr/local/bin/
su - user -c 'xinit'
shutdown -r now

Like the preceding file, this will also need to be chmodded a+x.

Hooks script

We need to do a number of things to the chroot before it is made into a binary image. Those things could either be done manually (when the helper scripts drop us off into a shell), or they can be done automatically through a hooks script. It is a good idea to still have live-helper drop us into a shell just so we can check to see if everything is in place and just as we want it to be (debian-live is beta software, so it is not a bad idea to double-check what it is doing).

Fire up emacs and place the following code into a buffer (edit as you see fit, but those question marks can stay just the way they are)


set -e

# change ownership of the user's home directory to proper uid/gid (999)
chown -R 999:999 /home/user

# make the following files immutable (unchangeable by anyone)
chattr +i /home/user/.mozilla/firefox/????????.default/localstore.rdf
chattr +i /home/user/.mozilla/firefox/????????.default/extensions.ini

# uncomment the line below this one to disable plugin installation
#chown -R root:root /home/user/.mozilla/plugins

# recursively fix ownership of the following directories
chown -R root:root /etc/init.d /usr/local/bin

# fix ownership of the following files and directories
chown root:root /usr /usr/local /etc /etc/rc.local /home

# if kiosk firewall script exists, then set it to run at boot 
if [ -f /etc/init.d/kiosk.fw ]
        update-rc.d kiosk.fw start 37 S .

# I am not completely sure that these files need to be there
# after removing them
rm /usr/lib/iceweasel/components/nsFilePicker.js
touch /usr/lib/iceweasel/components/nsFilePicker.js
rm /usr/lib/iceweasel/components/nsHelperAppDlg.js
touch /usr/lib/iceweasel/components/nsHelperAppDlg.js
rm /usr/lib/iceweasel/components/FeedProcessor.js
touch /usr/lib/iceweasel/components/FeedProcessor.js
rm /usr/lib/iceweasel/components/nsSessionStore.js
touch /usr/lib/iceweasel/components/nsSessionStore.js

If you disable plugin installation, users will not be able to install a flash player (in your situation this may be a good thing, so it is up to you). Save the buffer as "/home/user/kioskhook". You may have noticed that there is a reference to a firewall script in there. If you wish to include a firewall, it would be a good idea to create it and save it as "/home/user/kiosk.fw".

Let the fun begin!

You might want to tar up the pertinent files inside your home directory as follows:

$ cd && mkdir -p includes/home/user includes/etc/init.d includes/usr/local/bin
$ cp -r .mozilla .xscreensaver advert-images includes/home/user
$ cp xinitrc includes/home/user/.xinitrc
$ cp kiosk.fw includes/etc/init.d
$ cp rc.local includes/etc
$ cp includes/usr/local/bin
$ cd includes && tar cvzf ../includes.tar.gz ./*

We are now ready to start building our debian live configuration. Execute the following commands:

$ mkdir ~/live-kiosk && cd ~/live-kiosk
$ lh_config --apt apt --apt-recommends disabled --binary-images usb-hdd \
  --debconf-frontend dialog --debconf-priority low --hostname kiosk \
  --interactive shell --bootloader syslinux --bootstrap-flavour minimal \
  --distribution etch --linux-flavours 686 --hooks "mini kioskhook" \
  --sections "main" --union-filesystem unionfs \
  --syslinux-timeout 5

lh_config is what creates a base config tree for us. "--apt apt" tells it to choose apt instead of aptitude. "--apt-recommends disabled" will disable something to do with either prompting or automatic installation of reccomends (I forget which). "--binary-images usb-hdd" will tell debian-live to create a usb drive disk image as the final result. "--debconf-frontend dialog" will prompt you in a dialog for all debconf queries. "--debconf-priority low" sets the debconf priority. "--hostname kiosk" will set the hostname. "--interactive shell" will make sure we get a shell in the chroot before the binary image is created. "--bootloader syslinux" will tell debian-live which bootloader to use (GRUB might be an option too now, but i am unsure). "--bootstrap-flavour minimal" is important. "--distribution etch" ... using etch or lenny is up to you. I would suggest the stable branch, always. "--linux-flavours 686" should be self-explanitory, important. "--hooks "mini kioskhook"" defines the hooks to use. "--sections "main"" mentions the repository sections which are to be queried for packages by apt-get. "--union-filesystem unionfs" use unionfs instad of aufs to ease complications. "--syslinux-timeout 5" wait 5 miliseconds before timing out syslinux and booting.

$ cp /usr/share/live-helper/hooks/mini ~/kioskhook ./config/chroot_local-hooks

/usr/share/live-helper/hooks/mini ought to be the right one to use. Take a look inside and see what it does (essentially just strips unneeded stuff: man pages, locales, log files, apt cache).

$ emacs -nw config/chroot

Remove "standard" from the line which says LH_PACKAGES_LISTS. Leave only the double-quotes after it. Make that one line look like this:



$ cat >> config/chroot_local-packageslists/namedoesnotmatter << EOF
#below are optional
#iproute is for fwbuilder scripts compatability

What we just did was create a file whose name does not have any meaning, but the contents of which will tell debian-live what packages need to be included in the live distribution. All dependencies will be met.

$ cd ~/live-kiosk/config/chroot_local-includes
$ tar xvzf ~/includes.tar.gz

After this, take a good look and see if everything is in place. The next commands will set us up for building the image.

$ cd ~/live-kiosk
$ su -
# cd /home/user/live-kiosk
# lh_build --force

Stay put. There will be a lot of stuff happening. You will be prompted to make decisions (hint: set a locale, and make it the default. Allow anybody to run X.. You will then be presented with a shell. When the prompt appears you will be in the chroot environment. Check everything: permissions, file placement, manpages... Check them twice, then check them again. Run `df`. While you are in the chroot, edit the sudoers file (do not allow anyone to sudo!) and lock the root account by typing the following:

# visudo
# passwd -l root

Take a look at /etc/inittab and see if it is what you want. When you are satisfied and/or bored to tears, hit ctrl-d. More stuff will happen. It will build the binary image. After it is done, check the size of the image:

# ls -al binary.img

If it is below 90MB or above 250MB then something went wrong. If it is around 110-150MB then it is prime! Continue.

# logout
$ qemu -hda binary.img

Test to your heart's content! If satisfied, then you have done well. Plug in your USB key and Continue.

$ su -
# mv binary.img kiosk`date +%s`.img
# dd if=kioskNNNNNNNNNN.img of=/dev/your_usb_key_device_file

Please, PLEASE *P*L*E*A*S*E* make sure that the device file is correct for the USB key you wish to erase. That last command WILL erase all data on the key!

Some Notes before you leave:

If you happen to want to do a 'lh_clean' after doing an 'lh_build', then you probably ought to fix the immutable files in the chroot.

Where do we go from here?

If you would like to share your suggestions, comments, improvements, death threats, kind words of encouragement or just plain old "thanks", please email me at Thanks.

James Barrett
Last modified: Thu Nov 15 01:59:19 EDT 2007