Errements agronomiques
Un peu de technossiatif
Un peu de géopolisocial
Des projets seul ou pas
Articles divers
Infos
Ca fait quelques années que j'utilise Debian + Linux pour faire des routeurs, avec des petites transformations. Au fil du temps j'en suis venu à modifier le système pour le faire tenir sur une clé USB (en read-only pour qu'elle ne défaille pas trop vite), parce que les accès disque sur un routeur c'est rare, et que quasiment tout peut tenir dans la RAM.
On obtient ainsi un routeur efficace souple et puissant, sur lequel l'upgrade est un simple changement de clé.
A l'occasion d'une Nième modification, j'ai fait cette petite doc.
On a besoin de - une Debian générique avec qemu/kvm (appelée ci-dessous «generic») - un disque ou une clé USB de 4 Go comme cible
Tout le système sera en read-only dans /, environ 3 Go.
Une petite partition (moins de 1 Go) sera montée en read-write, de temps en temps, sur /cfg. Elle contiendra les modifications locales de /etc par rapport à la la config de base qui elle est contenue dans /conf/base/etc.
A chaque boot,
Quand une modif est faite localement dans /etc, elle est sauvegardée dans /cfg soit par un appel explicite de «save_cfg -a», soit quand le shell root quitte.
En parallèle, le système est backupé classiquement par rsync depuis un serveur de fichiers.
Je passe sur l'installation d'un système de base. Ici je suppose qu'on a déjà une image avec une Debian de base installée dessus à portée de la main et je ne m'intéresse à ce qui est spécifique à en faire un routeur en read-only.
Premièrement, un système de base sur la première partition.
On peut le faire par exemple a partir d'une image de VM qemu/kvm d'une debian de base, en la clonant.
TARGET_DISK=<chemin du volume> fdisk -ul $TARGET_DISK mount -o loop,offset=$((SECTOR_SIZE * PARTITION_START)) -t ext3 $TARGET_DISK /mnt
A partir de là on va travailler depuis l'intérieur : chroot /mnt bash
On change le nom pour éviter de ne plus savoir ou on se trouve par la suite :
hostname NEW_NAME echo NEW_NAME > /etc/hostname
on RAZ les règles persistences de nommage d'interfaces de udev, et on met en place la config qui va bien pour eth0 (à adapter selon les besoins bien entendu)…
cp /dev/null /etc/udev/rules.d/70-persistent-net.rules cat <<EOF >/etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.0.0.100 netmask 255.255.255.0 broadcast 10.0.0.255 gateway 10.0.0.253 EOF
C'est aussi l'occasion de regénérer les clés SSH locales :
rm /etc/ssh/ssh_host_* dpkg-reconfigure openssh-server
Editer dans /etc/defautl/grub :
GRUB_CMDLINE_LINUX_DEFAULT="" # supprimer "quiet" GRUB_CMDLINE_LINUX="console=ttyS0,38400n8 console=tty0"
idem, ne pas oublier de reconstruire les binaires de /boot/grub avec : update-grub
Un reboot est probablement indiqué à ce stade, pour faire simple.
Ensuite on crée un répertoire /conf/base dans la racine, et un point de montage pour /cfg
mkdir -p /conf/base mkdir /cfg
On installe les packages qui vont bien
aptitude install quagga screen iptraf less vlan sudo vim
Il faut modifier la séquence d'init pour réaliser les opérations suivantes
/etc/init.d/ramfs
#!/bin/sh ### BEGIN INIT INFO # Provides: etc # Required-Start: checkfs # Required-Stop: # Default-Start: S # Default-Stop: # Short-Description: Mount all filesystems. # Description: ### END INIT INFO mount -t tmpfs -o size=2M varlock /var/lock mount -t tmpfs -o size=2M varrun /var/run mount -t tmpfs -o size=16M varlog /var/log mount -t tmpfs -o size=16M tmp /tmp cd /var/run; mkdir mdadm network sreen sshd quagga chown -R quagga: quagga
Il faut aussi modifier mountall
pour qu'il dépende de ramfs
sed -e 's/Required-Start:.*/Required-Start: ramfs/' -i mountall.sh
/etc/init.d/etcfs
#!/bin/sh ### BEGIN INIT INFO # Provides: etcfs # Required-Start: mountall-bootclean # Required-Stop: # Default-Start: S # Default-Stop: # Short-Description: Mount all filesystems. # Description: ### END INIT INFO mount -t tmpfs -o size=64M etc /etc cp -a /conf/base/etc/* /etc/ mount /cfg cd /cfg tar cf - * | (cd /etc; tar xf -) cd umount /cfg
/etc/init.d/ro-root
#!/bin/sh ### BEGIN INIT INFO # Provides: ro-root # Required-Start: $all # Required-Stop: # Default-Start: S # Default-Stop: # Short-Description: Mount all filesystems. # Description: ### END INIT INFO mount -o remount,ro /
Et ne pas oublier ensuite recontruire les dépendance :
cd /etc/init.d/; insserv *
Editer fstab pour simplifier la suite des opérations de montage/démontage
cat >/etc/fstab <<EOF/dev/sda1 / ext2 errors=remount-ro 0 1 /dev/sda2 /cfg ext3 noauto 0 0 EOF
Enfin, faire une copie de /etc dans /conf/base/etc, ce sera la config « de base » et toutes les différences seront réputées « locales » (donc propres à cette machine en particulier) et donc copiées dans /cfg ensuite.
[ -d /conf/base/etc ] && rm -Rf /conf/base/etc cd /; cp -a /etc /conf/base/etc
A placer dans /usr/local/sbin/save_cfg
:
#!/bin/sh # -a : apply # -c : cron # -d : apply # -g : get conf # -h : help # -v : verbose #set -x DEBUG=false #DEBUG=true APPLY=false DIFF=false CRON=false NEWFILE="" SSHKEY=/etc/ssh/id_dsa_savecfg TLOGIN="savecfg" THOST="giw3.gixe.net" target="-i $SSHKEY $TLOGIN@$THOST" echo_debug ( ) ( if [ $DEBUG = true ] ; then echo $1 fi ) while getopts "acdghvf" flag do case "$flag" in a) echo_debug "flag $flag set" ; APPLY=true ; shift;; c) echo_debug "flag $flag set" ; CRON=true ; shift;; d) echo_debug "flag $flag set" ; DIFF=true ; shift;; f) echo_debug "flag $flag set" ; shift; NEWFILE=$1; echo_debug " with argument $NEWFILE" ; shift;; g) echo_debug "flag $flag set" ; trap "/bin/umount /cfg" 1 2 15 EXIT /bin/mount /cfg ( cd / ssh $target /root/make_cfg $2 | tar xf - ) umount /cfg trap "" 1 2 15 EXIT exit 0 ;; h) echo_debug "flag $flag set" ; echo usage : save_cfg [ -adhv | -f <file> ] ; echo " -a : apply - apply the modification" ; echo " -f <file>: add local file to /cfg" ; echo " -c : cron - don't print no save done warning " ; echo " -d : diff - output the diff between changes" ; echo " -g host.gitoyen.net : get conf, full restore /cfg " ; echo " -h : help - display this " ; echo " -v : verbose " ; exit 0 ;; v) echo_debug "flag $flag set" ; #echo flag $flag set ; DEBUG=true ; shift;; *) echo usage : save_cfg [-adhv] exit 0 ;; esac done cmp_stat ( ) ( if [ "$(/usr/bin/stat --format "%a:%B:%Y:%U:%G" $1)" \ = "$(/usr/bin/stat --format "%a:%B:%Y:%U:%G" $2)" ] ; then return 0 else return 1 fi ) cmp_file ( ) ( if cmp_stat $1 $2 && /usr/bin/cmp -s $1 $2 ; then return 0 else return 1 fi ) backup_file ( ) ( if [ -e $(/usr/bin/dirname /cfg/$1) ] ; then if [ -f $1 ] ; then [ $APPLY = true ] && /bin/cp -pfv /etc/$1 /cfg/$1 [ $APPLY = false ] && echo "++ /etc/$1 -> /cfg/$1" else [ $APPLY = true ] && /bin/cp -Rpfv /etc/$1 /cfg/$1 [ $APPLY = false ] && echo "+ /etc/$1/ -> /cfg/$1/" fi else backup_file $(dirname $1) [ $APPLY = false ] && echo "+ /etc/$1/ -> /cfg/$1/" fi ) trap "/bin/umount /cfg" 1 2 15 EXIT /bin/mount /cfg ( #set -x cd /etc if [ -n "$NEWFILE" ]; then NF=${NEWFILE#/etc/} if [ -r /etc/$NF ]; then echo "** adding the following files to /cfg:" tar cvf - $NF | \ (cd /cfg; if [ $APPLY = true ]; then tar xf - else cat >/dev/null fi) fi fi for i in `/usr/bin/find * -type f | grep -v mtab` do if [ -f /cfg/$i ] then echo_debug "File /cfg/$i exist " if cmp_file /etc/$i /cfg/$i ; then echo_debug "|" echo_debug "--> /etc/$i /cfg/$i identical nothing to do" else echo_debug "|" echo_debug "--> Need to backup /etc/$i -> /cfg" [ $DIFF = true ] && /usr/bin/diff /etc/$i /cfg/$i backup_file $i fi else echo_debug "No File /cfg/$i " if [ -f /conf/base/etc/$i ] && cmp_file /etc/$i /conf/base/etc/$i ; then echo_debug "|" echo_debug "--> /etc/$i /conf/base/etc/$i identical nothing to do" else echo_debug "|" echo_debug "--> Need to backup /etc/$i -> /cfg" if [ -f /conf/base/etc/$i ] ; then [ $DIFF = true ] && /usr/bin/diff /etc/$i /conf/base/etc/$i else [ $DIFF = true ] && echo " This is a file creation" fi backup_file $i fi fi done cd / #[ $APPLY = true ] && tar cf - cfg | ssh $target /root/commit $(hostname) ) umount /cfg trap "" 1 2 15 EXIT if [ $APPLY = false ] && ! [ $CRON = true ] then echo "**************************" echo "* WARNING, NO SAVE DONE *" echo "**************************" echo "" echo "Launch '/root/save_cfg -a' if you want to apply." fi
L'essentiel de ce script avait été écrit pour les routeurs de Gitoyen, qui fonctionne un peu différement avec des configs centralisée sous SVN sur un serveur distant, et les routeurs sous nanobsd.
Pour ne pas oublier d'invoquer «save_cfg -a» on peut l'appeler dans /root/.bash_logout
, ainsi la sauvegarde des modifications locales est faite à chaque fois que root se déloge, tout simplement. Une option utile serait de rendre le script interactif en pareil cas pour pouvoir l'inhiber si besoin (état instable à ne pas sauvegarder par exemple).
#!/bin/sh /usr/local/sbin/save_cfg -a
A partir de là on a une image utilisable comme routeur.
On sort du chroot, on démonte /mnt et on peut recopier l'image sur une clé USB et tâcher de booter dessus pour voir le résultat.