Fun with FreeBSD

Simon Strandgaard

Thomas Gielfeldt


Table of Contents

Introduction
Simon's Setup
Using The Vinum Volume Manager
Zap The Disk
Mirror Volume (RAID1)
Starting Vinum At Boot
Adding a subdisk
Running A CVS Server
Setup Repositories
checkout attempt
Nightly Backup Of Repositories
The Backupbox Setup
The Server Setup
Restore from backup
Network Address Translation
Redirect port 80 to WebServer
Redirect port 22 to CVS-Server
Samba
Realtek 8019AS ISA netcard
Requirements
Install
Post install
Text Terminal

Introduction

This documents describes how we (Simon/Thomas) have set'ed up our FreeBSD systems. It is primary intented as a reminder for ourselves, how did we do it, so that maintainence is easier. We hope this document can be educational for others. If you have any requests/problems then feel free to mail us.

We only talk about FreeBSD-5.1.

Simon's Setup

Simons Machines

Desktop

A silent pentium133™ which functions only as a X-terminal through which I can connect to my server. The only time when its not turned on, is when I sleep.

Gateway

A noisy pentium133™ which functions as a Gateway. It is always turned on. Because of the noise its located in my wardrobe. It preserves also a VPN connection, so that my machines is on the same subnet as Thomas'es machines.

# /etc/rc.conf
gateway_enable="YES"
hostname="gateway.neoneye.home"
ifconfig_rl0="inet 10.0.0.1  netmask 255.255.0.0"
ifconfig_ep0="DHCP"
named_enable="YES"
Server

A noisy pentium350™ which is my working horse. It is always turned on. Because of the noise its located in my wardrobe. Keywords: vinum, cvs-server, xdmcp, apache.

# /etc/rc.conf
hostname="server.neoneye.home"
ifconfig_xl0="inet 10.0.0.10 netmask 255.255.0.0 media 100baseTX"
defaultrouter="10.0.0.1"
start_vinum="YES"

Using The Vinum Volume Manager

When I setup'ed vinum, I had severe problems with disklabeling my partions correct. When I discovered that vinum doesn't like to be on the C: partition, and thus instead placed it on the E: partition, then everything was working perfectly.

Zap The Disk

  1. Warning this is dangerous, it will erase all your data!

    server# dd if=/dev/zero count=128 of=/dev/ad2
    server# fdisk -I /dev/ad2
    server# disklabel -w /dev/ad2
    server#
    

    Just ignore the warnings.

  2. Invoke disklabel -e /dev/ad2 and change from:

    # /dev/ad2:
    8 partitions:
    #       size offset fstype [fsize bsize bps/cpg]
     c: 80418240      0 unused  0     0      # "raw" part, don't edit
    

    To

    # /dev/ad2:
    8 partitions:
    #       size offset fstype [fsize bsize bps/cpg]
     c:        *      * unused  0     0      # "raw" part, don't edit
     e: 80418240      * vinum                    
    

    (important) the vinum man-pages say: “partition ``c'' represents the whole disk and should not be used for any other purpose.” Of casue I overlooked this, which were terrible confusing, therefore my advice is to use partition 'e:' instead!

Let's verify that everything is OK.

server# ls /dev/ad2*
/dev/ad2   /dev/ad2c   /dev/ad2e
server# disklabel /dev/ad2
# /dev/ad2:
8 partitions:
#       size offset fstype [fsize bsize bps/cpg]
 c: 80418240      0 unused  0     0      # "raw" part, don't edit
 e: 80418240      0 vinum                    
server#

Mirror Volume (RAID1)

  1. Create a config1 file with the following content:

    drive a device /dev/ad1e
    drive b device /dev/ad2e
    volume reliable setupstate 
      plex org concat
        sd length 100m drive a
      plex org concat
        sd length 100m drive b
    
  2. server# vinum create -f config1
    2 drives:
    D b                     State: up       /dev/ad2e       A: 229/329 MB (69%)
    D a                     State: up       /dev/ad1e       A: 39166/39266 MB (99%)
    
    1 volumes:
    V reliable              State: up       Plexes:       2 Size:        100 MB
    
    2 plexes:
    P reliable.p0         C State: up       Subdisks:     1 Size:        100 MB
    P reliable.p1         C State: up       Subdisks:     1 Size:        100 MB
    
    2 subdisks:
    S reliable.p0.s0        State: up       D: a            Size:        100 MB
    S reliable.p1.s0        State: up       D: b            Size:        100 MB
    server#
    
  3. Lets validate that its actually up

    server# ls /dev/vinum/
    control   controld  plex/     reliable  sd/
    server#
    

    Our 'reliable' volume seems to be up!

  4. server# newfs /dev/vinum/reliable
    server#
    

Our 'reliable' volume is now ready to use

server# mount /dev/vinum/reliable /mnt
server# df -h
Filesystem            Size   Used  Avail Capacity  Mounted on
/dev/ad0s1a           248M    53M   175M    23%    /
devfs                 1.0K   1.0K     0B   100%    /dev
/dev/ad0s1e           248M    18K   228M     0%    /tmp
/dev/ad0s1f           8.2G   3.0G   4.5G    40%    /usr
/dev/ad0s1d           248M   9.1M   219M     4%    /var
/dev/vinum/reliable    97M   4.5M    84M     5%    /mnt
server#

Starting Vinum At Boot

We need only to modify 2 places.

  1. Append to /etc/rc.local

    start_vinum="YES"
    
  2. Append to /etc/fstab

    /dev/vinum/reliable     /reliable       ufs     rw              2       2
    

Welcome to vinum world.

Adding a subdisk

Placeholder.

Running A CVS Server

Let's build a reliable CVS server. Let's figure out how to manage permissions..etc.

If your server is behind a gateway, and you want CVS to be externaly accessable, then you will need to setup special ipnat.rules.

Setup Repositories

Setup cvs-groups for in /etc/group

cvs_xeet:*:800:depth,neoneye,jonaz
cvs_firma:*:801:depth,neoneye,thomas
thomas:*:1000:
neoneye:*:1001:
depth:*:1002:
jonaz:*:1003:

As root execute following command sequence

cd /usr/local
mkdir cvsroot
cd /usr/local/cvsroot

Install your repositories here!

mkdir mycvsrepository
cvs -d /reliable/mycvsrepository init

Ensure that the permissions is correct.

> pwd
/usr/local/cvsroot
> ls -la
total 10
drwxr-xr-x   5 root  wheel      512 Nov 27 02:01 .
drwxr-xr-x  15 root  wheel      512 Feb  4 10:28 ..
drwxrwxr-x   5 root  cvs_firma  512 Mar 13 00:00 firma
drwxrwxr-x   5 root  neoneye    512 Mar 13 00:00 neoneye_private
drwxrwxr-x  14 root  cvs_xeet   512 Mar 13 00:00 xeet_bombrun_code

What if the group/permission is wrong?

cd /usr/local/cvsroot/firma
chgrp -R cvs_xeet .
chmod ug+rwx . CVSROOT
chmod -R g+w *

checkout attempt

On some other machine try issuing a cvs checkout, to see if everything works.

setenv CVS_RSH ssh
cvs -d :ext:neoneye@10.0.0.122:/usr/local/cvsroot/xeet_bombrun_code co .

Congratulations.. The repository should now be operational :-)

Nightly Backup Of Repositories

We have some cvs-data which we really don't want to loose, thus we want a backup every night. To be extra safe we want the backup transfered to a remote location.

Its also important that we are able to restore from a backup, thus we discuss how to do restoration.

The Backupbox Setup

  1. Create a cvsbackup user with a homedir and no password:

    root@backupbox# pw useradd cvsbackup -m -h -
    root@backupbox# 
    
  2. Validate that ruby is accessable from the cvsbackup account.

    cvsbackup@backupbox> ruby -v
    ruby 1.8.0 (2003-08-04) [i386-freebsd5.1]
    cvsbackup@backupbox> 
    
  3. Create a directory into which the backups has to be delivered:

    cvsbackup@backupbox> pwd
    /home/cvsbackup
    cvsbackup@backupbox> mkdir backup
    cvsbackup@backupbox> 
    
  4. Install this script as /home/cvsbackup/remove.sh

    #!/bin/sh
    # REMOVE OLD BACKUPs
    
    PATH=$PATH:~/bin:/usr/local/bin
    #echo $PATH > env
    
    echo "-------------------------------------" >> log
    date >> log
    ruby remove.rb 1> stdout 2> errout
    cat stdout >> log
    cat errout >> log
    
  5. Install this script as /home/cvsbackup/remove.rb

    # purpose: 
    # remove old files (preserve the latest 5 files).
    N = 5
    Dir.chdir("backup")
    repo = Dir["*"].map{|i|`ls -t #{i}/*`.split("\n")}
    remo = repo.map{|i|i[N..-1]}.flatten.compact
    system("rm -rf "+remo.join(" ")) unless remo.empty?
    #puts "FILES="+repo.inspect
    puts "REMOVED="+remo.inspect
    
  6. Add this to your crontab (crontab -e). This will keep the 5 newest files (remove old files) for all subdirs in backup. Other files will be removed. The cronjob will be executed at every midnight.

    0 0 * * * ~/remove.sh
    

    Validate that your crontab is installed correct

    cvsbackup@backupbox> crontab -l
    0 0 * * * ~/remove.sh
    cvsbackup@backupbox>
    

The backupbox is now ready to recieve a daily incoming backupdir. You can keep track of status by issuing tail -f log.

The Server Setup

The backup script will be executed by root. An entry in its log file (/root/backup/log), look like this:

Fri Sep 12 00:00:01 CEST 2003
NEONEY'S CVS-BACKUP TOOL
source="/reliable/"  
dest="/root/backup/todays_snapshot_of_cvs/"  
stamp="20030912_00"
dropzone="cvsbackup@10.0.0.1:backup/."
COMPRESSING REPOSITORIES
#3 public_documents ... OK (13442 bytes)
#2 dotfiles_neoneye ... OK (32076 bytes)
#1 code_neoneye ... OK (25667 bytes)
TRANSFERING REPOSITORIES
DONE

A good overview of what is going on. Follow this procedure in order to set this cronjob up:

  1. Validate that cvslock and ruby is accessable from the root account.

    root@server# cvslock
    cvslock: There's no directory parameter
    root@server# ruby -v
    ruby 1.8.0 (2003-08-04) [i386-freebsd5.1]
    root@server# 
    
  2. Make sure that SSH with sshkeys is working, that you can create a passwordless SSH connection, from the CVS-server to the remote-backup-drop-location.

    root@server# ssh cvsbackup@backupbox
    cvsbackup@backupbox> exit
    root@server#
    
  3. Create a backup directory

    root@server# pwd
    /root
    root@server# mkdir backup
    root@server#
    
  4. Install this script as /root/backup/backup.sh

    #!/bin/sh
    # DO BACKUP OF REPOSITORIES
    
    cd /root/backup
    
    PATH=$PATH:~/bin:/usr/local/bin:/home/neoneye/bin
    #echo $PATH > env
    
    echo "-------------------------------------" >> log
    date >> log
    ruby backup.rb 1> stdout 2> errout
    cat stdout >> log
    cat errout >> log
    
  5. Install this script as /root/backup/backup.rb

    require 'fileutils'
    
    class Backup
        # these constants must be absolute paths, 
        # Ruby doesn't like "~/stuff" kind of paths! 
        DIR_SOURCE = "/reliable/"
        DIR_DEST = "/root/backup/todays_snapshot_of_cvs/"
        DROPZONE = "cvsbackup@10.0.0.1:backup/."
    
        def initialize
            @stamp = prepare_stamp
            prepare_dirs
        end
        def prepare_stamp
            Time.now.strftime("%Y%m%d_%H")
        end
        def prepare_dirs
            if FileTest.exists?(DIR_DEST) 
                FileUtils.rm_r DIR_DEST, :force => true
            end
            FileUtils.mkdir_p DIR_DEST, :mode => 0700
        end
        def names # repository_names
            Dir.chdir(DIR_SOURCE)
            Dir["*"]
        end
        def backup(name)
            dir_dest = DIR_DEST + name + "/"
            dest = dir_dest + @stamp + ".tar.gz"
            cmd_nest = "tar cfz #{dest} #{name}"
            cmd = "cvslock -q -d #{name} -c \"#{cmd_nest}\" ."
    
            # tar doesn't like leading '/' (slashes)
            # thus we must chdir to DIR_SOURCE
            Dir.chdir(DIR_SOURCE)
            FileUtils.mkdir_p dir_dest, :mode => 0700
            system(cmd)
    
            FileTest.size(dest) # return number of bytes
        end
        def pretty_backup
            ary = names
            n = ary.size
            ary.each do |name| 
                print "##{n} #{name} ... "
                $stdout.flush
                bytes = backup(name) 
                puts "OK (#{bytes} bytes)"
                n -= 1
            end
        end
        def transfer
            # transfer to remote host
            Dir.chdir(DIR_DEST)
            system("scp -rBq * #{DROPZONE}")
        end
        def info
            <<MSG
    source=#{DIR_SOURCE.inspect}  
    dest=#{DIR_DEST.inspect}  
    stamp=#{@stamp.inspect}
    dropzone=#{DROPZONE.inspect}
    MSG
        end
        def Backup.execute
            i = Backup.new
            puts "NEONEY'S CVS-BACKUP TOOL"
            puts i.info
            puts "COMPRESSING REPOSITORIES"
            i.pretty_backup
            puts "TRANSFERING REPOSITORIES"
            i.transfer
            puts "DONE"
        end
    end
    
    Backup.execute
    
  6. edit the crontab (crontab -e).

    0 0 * * * /bin/sh /root/backup/backup.sh
    

The server should now deliver tarballs to the backupbox at daily basis. You can monitor the log file (tail -f /root/backup/log) in order to keep an eye on how things is going.

Restore from backup

You will see that in every subdir in the repository, cvslock has created #cvs.rfl.cvslock.slowserver.home.432 In order to reenable the repository, then you have to delete those read-lock files.

find . -name "#cvs.rfl.cvslock*" -exec rm -f {} \;

Congratulations.. the cvslocks is now gone, exactly as we wanted.

Network Address Translation

Let's first have a look at how my current (final) /etc/ipnat.rules looks like, shall we:

neoneye@gateway> cat /etc/ipnat.rules 
## rename outgoing (ftp, all) trafic
map xl0 10.0.0.1/24 -> 0/32 proxy port ftp ftp/tcp
map xl0 10.0.0.1/24 -> 0/32 portmap tcp/udp auto
map xl0 10.0.0.1/24 -> 0/32

## rename incoming (http) trafic
rdr xl0 0.0.0.0/0 port 80 -> 10.0.0.253 port 80 tcp

## rename incoming (ssh) trafic
rdr xl0 0.0.0.0/0 port 666 -> 10.0.0.1 port 22 tcp
rdr xl0 0.0.0.0/0 port 22 -> 10.0.0.253 port 22 tcp
neoneye@gateway>

Redirect port 80 to WebServer

Append the following to /etc/ipnat.rules

rdr xl0 0.0.0.0/0 port 80 -> 10.0.0.253 port 80 tcp

Tell ipnat to reread its configuration (ipnat -CF -f /etc/ipnat.rules).

Redirect port 22 to CVS-Server

In order to do cvs -d :ext:developername@publicip, we need to redirect SSH. Add this to /usr/ipnat.rules.

rdr xl0 0.0.0.0/0 port 666 -> 10.0.0.1 port 22 tcp
rdr xl0 0.0.0.0/0 port 22 -> 10.0.0.253 port 22 tcp

maybe this can be done smarter by having a 'cvs' subdomaine in DNS ?

Samba

This is how my /usr/local/etc/smb.conf look like:

[global]
  guest ok = yes
  workgroup = MYWGRP
  security = share
  remote announce = 10.0.0.255/WORKGROUP

[unsafe]
  comment = a unsafe disk
  path = /unsafe
  read only = yes
  public = yes

Realtek 8019AS ISA netcard

Procedure for install FreeBSD5.0+ via an old Realtek 8019AS™ ISA card!

Requirements

  • Obtain the RSET8019.EXE program, this can be downloaded at Realtek's homepage.

  • A DOS-boot-floppy, which allows you to execute the RSET8019.EXE program.

Install

Following steps will install FreeBSD via your realtek card.

  1. RSET8019.EXE, Switch to jumperless mode and write down settings(irq, port)

  2. FreeBSD startup

    set hint.ed.0.disabled=0
    set hint.ed.0.port=0x280
    set hint.ed.0.maddr=0xcc000
    boot -v
    
  3. FreeBSD sysinstall, You should now be able to select a media "ed0".

Congratulations.. you have now installed FreeBSD.

Post install

  1. open the file /boot/device.hints with your favorite editor

  2. The hint.ed.0.* should look like this

    #hint.ed.0.disabled="1"
    hint.ed.0.port="0x280"
    hint.ed.0.irq="10"
    #hint.ed.0.maddr="0xd8000"
    hint.ed.0.maddr="0xcc000"
    
  3. reboot

Congratulations .. your machine is now wired :-)

Text Terminal

I have an old WYSE 520ES™ terminal, connected to my Desktop machine.

TODO: the terminal has 25 lines, but only 24 are usable. Only VIM are able to use the last line. Why?

In order to enable serial-port1, I had to do this. I could not get 38400 baud working, it outputted garbage. Therefore I had to stick to 19200 baud. Maybe its because of bad flowcontrol, I don't know? You can request init to re-read the ttys configuration, by invoking kill -HUP 1.

# /etc/ttys @ desktop 
ttyd1   "/usr/libexec/getty std.19200"  wy85    on  secure

In order to get danish letters working correctly, I had to add:

# ~/.tcshrc @ desktop 
setenv LC_CTYPE da_DK.ISO_8859-1

The terminal configuration are:

# menu's on the WY520ES terminal
[DISPLAY]
  80 Columns
  Width Change = Clear
  Interpret Controls
  No Autowrap
  Jump Scroll
  Light Screen
  Cursor
  Cursor Blink Block
  No Status Display
  5x25 Pages
  24 Lines/Screen
  Vertical Coupling
  Page Coupling
  No Auto Resize Screen

[GENERAL]
  WY-85 Mode, 7-Bit Controls
  DECSCL = WY285
  Multinational Mode
  User-Defined Keys Locked
  User Features Locked
  Numeric Keypad
  Normal Cursor Keys
  No Newline
  UPSS ISO Latin-1
  ID Matches Personality
  When Available Update

[COMMUNICATIONS]
  COMM1: Transmit=19200
  COMM1: Recieve=Transmit
  COMM1: XOFF at 64
  COMM1: 8 Bits, No Parity
  COMM1: 2 Stop Bit
  No Local Echo
  Data Leads Only
  Disconnect, 2 s Delay
  Unlimited Transmit
  Auto Answerback
  Answerback
  Modem High Speed = Ignore
  Modem Low Speed = Ignore
  On Line

[KEYBOARD]
  Typewriter Keys
  Caps Lock
  Key Repeat
  Keyclick Off
  Margin Bell Off
  Warning Bell High
  Character Mode
  X BS/BS
  Local Compose
  Ignore Alt
  Fkeys, Definitions
  Program Function Keys
  Danish Keyboard

[GLOBAL]
  S1=Comm1
  Screen Saver
  Comm1=RS-232
  Printer Shared
  Setup=English