├── .dockerignore ├── .gitignore ├── docs ├── ipxe-tftp.service ├── OpenBSD.md └── ipxe_home_server.md ├── docker ├── Dockerfile └── run.sh ├── start.sh ├── init.sh ├── create-rootfs.md └── README.md /.dockerignore: -------------------------------------------------------------------------------- 1 | debian 2 | netboot 3 | voyage 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | debian 2 | voyage 3 | netboot 4 | -------------------------------------------------------------------------------- /docs/ipxe-tftp.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description= tftp pxe server container 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | 7 | [Service] 8 | ExecStartPre=-/usr/bin/docker kill ipxetftp 9 | ExecStartPre=-/usr/bin/docker rm ipxetftp 10 | ExecStart=/usr/bin/docker run --name ipxetftp --net=host -p 69:69/udp -v /opt/pxe-tftpd/netboot:/srv/tftp -t 3mdeb/pxe-server 11 | ExecStopPost=/usr/bin/docker stop -t 2 ipxetftp 12 | NotifyAccess=all 13 | 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | MAINTAINER Piotr Król 4 | 5 | RUN apt-get update && apt-get install -y --no-install-recommends \ 6 | apt-utils \ 7 | git \ 8 | vim \ 9 | tmux \ 10 | python \ 11 | python-dev \ 12 | ntpdate \ 13 | ca-certificates \ 14 | nfs-kernel-server \ 15 | nfs-common \ 16 | netbase \ 17 | udhcpd 18 | 19 | RUN mkdir -p /srv/nfs 20 | RUN mkdir -p /srv/http 21 | 22 | VOLUME /srv/nfs 23 | VOLUME /srv/http 24 | 25 | ADD run.sh /usr/local/bin/run.sh 26 | 27 | EXPOSE 111/tcp 111/udp 2049/tcp 2049/udp 32765/tcp 32765/udp 32766/tcp 32766/udp 32767/tcp 32767/udp 28 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ $? -ne 0 ]; then 6 | echo "ERROR: Unable to build container" 7 | exit 1 8 | fi 9 | 10 | sudo service nfs-kernel-server stop 11 | sudo service rpcbind stop 12 | 13 | docker run --rm --name pxeserver --privileged \ 14 | -p 111:111/tcp -p 2049:2049/tcp -p 8000:8000/tcp \ 15 | -p 627:627/tcp -p 627:627/udp -p 875:875/tcp -p 875:875/udp \ 16 | -p 892:892/tcp -p 892:892/udp -p 111:111/udp -p 2049:2049/udp \ 17 | -p 10053:10053/udp -p 10053:10053/tcp \ 18 | -p 32769:32769/tcp -p 32769:32769/udp \ 19 | -p 32765:32765/tcp -p 32765:32765/udp \ 20 | -p 32766:32766/tcp -p 32766:32766/udp \ 21 | -p 32767:32767/tcp -p 32767:32767/udp \ 22 | -v ${PWD}/netboot:/srv/http \ 23 | -v ${PWD}/debian/debian-stable:/srv/nfs/debian \ 24 | -v ${PWD}/voyage:/srv/nfs/voyage \ 25 | -v ${PWD}/xen:/srv/nfs/xen \ 26 | -t -i 3mdeb/pxe-server /bin/bash -c \ 27 | "bash /usr/local/bin/run.sh" 28 | 29 | 30 | -------------------------------------------------------------------------------- /docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | export_base="/srv/nfs/" 5 | HTTP_SRV="/srv/http/" 6 | 7 | ### Handle `docker stop` for graceful shutdown 8 | function shutdown { 9 | echo "- Shutting down nfs-server.." 10 | service nfs-kernel-server stop 11 | echo "- Nfs server is down" 12 | exit 0 13 | } 14 | 15 | trap "shutdown" SIGTERM 16 | #### 17 | 18 | echo "Export points:" 19 | echo "/srv/nfs *(rw,sync,fsid=0,no_subtree_check,no_root_squash)" | tee /etc/exports 20 | echo "/srv/nfs/debian *(rw,sync,no_subtree_check,no_root_squash)" | tee -a /etc/exports 21 | echo "/srv/nfs/xen *(rw,sync,no_subtree_check,no_root_squash)" | tee -a /etc/exports 22 | echo "/srv/nfs/voyage *(rw,sync,no_subtree_check,no_root_squash)" | tee -a /etc/exports 23 | echo "/srv/nfs/xen *(rw,sync,no_subtree_check,no_root_squash)" | tee -a /etc/exports 24 | 25 | read -a exports <<< "${@}" 26 | for export in "${exports[@]}"; do 27 | src=`echo "$export" | sed 's/^\///'` # trim the first '/' if given in export path 28 | src="$export_base$src" 29 | mkdir -p $src 30 | chmod 777 $src 31 | echo "$src *(rw,sync,no_subtree_check,no_root_squash)" | tee -a /etc/exports 32 | done 33 | 34 | echo -e "\n- Initializing nfs server.." 35 | mkdir -p /run/sendsigs.omit.d 36 | rpcbind -i 37 | # set static port to avoid using random ports by nfs 38 | rpc.statd --no-notify --port 32765 --outgoing-port 32766 39 | # force nfsv3 over udp due to TCP issues (investigating) 40 | rpc.nfsd -V3 -N2 -N4 -d 8 41 | rpc.mountd -V3 -N2 -N4 --port 32767 42 | # update exports and start nfs server 43 | exportfs -ra 44 | service nfs-kernel-server start 45 | 46 | echo "- Nfs server is up and running.." 47 | cd $HTTP_SRV 48 | python -m SimpleHTTPServer 8000 49 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Usage: 3 | # NFS_SRV_IP= ./init.sh 4 | 5 | ipvalid() { 6 | # Set up local variables 7 | local ip=$NFS_SRV_IP 8 | local IFS=.; local -a a=($ip) 9 | # Start with a regex format test 10 | [[ $ip =~ ^[0-9]+(\.[0-9]+){3}$ ]] || return 1 11 | # Test values of quads 12 | for quad in {0..3}; do 13 | [[ "${a[$quad]}" -gt 255 ]] && return 1 14 | done 15 | return 0 16 | } 17 | 18 | if [ -z "$NFS_SRV_IP" ]; then 19 | echo "Please provide NFS server ip as NFS_SRV_IP" 20 | exit 1 21 | fi 22 | 23 | if ipvalid "$NFS_SRV_IP"; then 24 | echo "NFS server ip ($NFS_SRV_IP) is valid" 25 | else 26 | echo "Wrong server ip address ($NFS_SRV_IP)" 27 | exit 1 28 | fi 29 | 30 | git clone https://github.com/3mdeb/netboot.git 31 | 32 | sed -i "s/replace_with_ip/$NFS_SRV_IP/g" ./netboot/menu.ipxe 33 | 34 | wget https://cloud.3mdeb.com/index.php/s/UQQVYrNIhg7ddwj/download -O kernels.tar.gz 35 | 36 | tar -xzvf kernels.tar.gz -C ./netboot && rm kernels.tar.gz 37 | 38 | wget https://cloud.3mdeb.com/index.php/s/9b8h6WmJcNsuB57/download -O debian-stable.tar.gz 39 | wget https://cloud.3mdeb.com/index.php/s/fzQ2FaRTdMvzXqO/download -O xen.tar.gz 40 | wget https://cloud.3mdeb.com/index.php/s/AQuUdsYkBzO9UJz/download -O core.tar.gz 41 | 42 | mkdir debian 43 | tar -xvpzf debian-stable.tar.gz -C ./debian --numeric-owner 44 | tar -xvpzf xen.tar.gz -C ./debian --numeric-owner 45 | 46 | mkdir voyage 47 | wget https://cloud.3mdeb.com/index.php/s/rUZPwRHOjxpSxN4/download -O voyage-0.11.0_amd64.tar.gz 48 | 49 | tar -xvpzf core.tar.gz -C ./netboot 50 | 51 | tar -xzvf voyage-0.11.0_amd64.tar.gz -C ./voyage 52 | rm voyage-0.11.0_amd64.tar.gz 53 | rm debian-stable.tar.gz 54 | rm xen.tar.gz 55 | rm core.tar.gz 56 | -------------------------------------------------------------------------------- /docs/OpenBSD.md: -------------------------------------------------------------------------------- 1 | Install OpenBSD over PXE 2 | ------------------------ 3 | 4 | 1. Obtain necessary files. 5 | 6 | Minimal set of files to perform network installation: 7 | 8 | ``` 9 | bsd.rd 10 | pxeboot 11 | ``` 12 | For example: 13 | 14 | ``` 15 | wget http://ftp.icm.edu.pl/pub/OpenBSD/5.9/amd64/bsd.rd 16 | wget http://ftp.icm.edu.pl/pub/OpenBSD/5.9/amd64/pxeboot 17 | ``` 18 | By default, those files should be placed directly in tftp server root 19 | directory. In our case it's `netboot` directory, which is mounted inside 20 | container at `/srv/tftp`. They can be also placed in different paths, such as 21 | `/OpenBSD/amd64`, but it needs to be stated later in configuration files. 22 | 23 | If only one system per server is concerned, you could directly boot using 24 | `pxeboot` as `filename` variable. However, we are using `pxelinux ` and we are 25 | interested in multiple systems. It is important to have the file end with .0, 26 | because the extension determines what pxelinux does with the file. The .0 tells 27 | pxelinux that it is a PXE image. 28 | 29 | ``` 30 | mv pxeboot pxeboot.0 31 | ``` 32 | 33 | 2. Provide paths to files. 34 | 35 | Originally, path to `pxeboot.0` should be entered in `pxelinux.cfg/default` 36 | file. However, we have used debian installer boot menu from 37 | `/debian-installer/i386/boot-screens/` and simply imported it into 38 | `pxelinux.cfg/default`: 39 | 40 | ``` 41 | include debian-installer/i386/boot-screens/menu.cfg 42 | ``` 43 | Menu entry for OpenBSD is provided in `txt.cfg` file: 44 | 45 | ``` 46 | label open-bsd 47 | menu label ^OpenBSD-5.9 48 | menu default 49 | kernel OpenBSD/amd64/pxeboot.0 50 | ``` 51 | Path to `bsd.rm` is set in `/etc/boot.conf`, which needs to be created. 52 | 53 | ``` 54 | boot tftp:/OpenBSD/amd64/bsd.rd 55 | ``` 56 | Note that it has to be in tftpd root directory, not the directory where bsd 57 | files are! 58 | 59 | 3. Boot configuration. 60 | 61 | Bootloader configuration is set through `/etc/boot.conf` file. In our case we 62 | need at least to enable serial port communication and set it's speed. 63 | 64 | ``` 65 | stty com0 115200 66 | set tty com0 67 | ``` 68 | 4. Installation. 69 | 70 | Installation process is mostly straightforward. Take a look at the choice of 71 | sets to install. Advised minimal sets are: 72 | 73 | ``` 74 | bsd (the kernel) - essential 75 | bsd.rd (RAM disk kernel) 76 | bsd.mp (multi-processor kernel) 77 | baseXX.tgz (OpenBSD base system) - essential 78 | ``` 79 | -------------------------------------------------------------------------------- /create-rootfs.md: -------------------------------------------------------------------------------- 1 | Debian rootfs 2 | ============= 3 | 4 | This paper describes procedure of creating a Debian rootfs for nfs server. 5 | 6 | #### Requirements 7 | 8 | 1. Installed `debootstrap` package: 9 | ``` 10 | sudo apt-get install -y debootstrap 11 | ``` 12 | 2. ~1GB free disk space. 13 | 14 | 15 | ## Creating base system 16 | 17 | Choose a directory where the rootfs should be created, for example: 18 | ``` 19 | mkdir debian-rootfs 20 | ``` 21 | 22 | To create the base system run following command: 23 | 24 | ``` 25 | sudo debootstrap --foreign --arch debian-rootfs http://deb.debian.org/debian 26 | ``` 27 | 28 | For example: 29 | 30 | ``` 31 | sudo debootstrap --foreign --arch amd64 stretch debian-rootfs http://deb.debian.org/debian 32 | ``` 33 | 34 | Debootstrap must be run as super user in order to create files with correct 35 | permissions etc. The command above will create basic file system and directories 36 | and files. `debootstrap` has also a second stage which must be run inside 37 | `chroot`. In order to finish creating filesystem run following command: 38 | 39 | ``` 40 | sudo chroot debian-rootfs 41 | /debootstrap/debootstrap --second-stage --verbose 42 | ``` 43 | 44 | The creation of basic filesystem is done if there were no errors till now. 45 | 46 | ## Customizing created system 47 | 48 | After debootstrapping the filesystem needs some configuration to satisfy the 49 | needs. It is necessary to set up network interfaces, mounts and a root password. 50 | 51 | Setting up network interfaces (inside chroot): 52 | 53 | ``` 54 | cat >> /etc/network/interfaces << EOF 55 | auto lo 56 | iface lo inet loopback 57 | auto eth0 58 | iface eth0 inet dhcp 59 | EOF 60 | ``` 61 | 62 | Setting up mounts (inside chroot): 63 | 64 | ``` 65 | cat > /etc/fstab << EOF 66 | /proc /proc proc defaults 0 0 67 | /sys /sys sysfs defaults 0 0 68 | EOF 69 | ``` 70 | 71 | Set root password (inside chroot): 72 | 73 | ``` 74 | passwd 75 | ``` 76 | 77 | ## Package configuration 78 | 79 | Probably You will want to install some packages, You can do it now inside chroot 80 | by running: 81 | ``` 82 | apt-get install -y ... 83 | ``` 84 | or just boot the system with PXE and run it on target machine. 85 | 86 | There is a guide how to setup pxe-server [here](README.md), but it is customized 87 | for APU2. 88 | 89 | ### Packages for APU2 90 | 91 | Install basic packages necessary for developer use (compiling from source, 92 | coreboot etc.): 93 | 94 | ``` 95 | apt-get install -y nfs-common locales \ 96 | sudo bc ssh ntpdate gettext \ 97 | autoconf wpasupplicant dialog \ 98 | makedev binutils apt-utils \ 99 | git vim tmux python \ 100 | ca-certificates \ 101 | python-dev ntpdate \ 102 | build-essential \ 103 | iasl \ 104 | m4 \ 105 | flex \ 106 | bison \ 107 | gdb \ 108 | doxygen \ 109 | ncurses-dev \ 110 | cmake \ 111 | make \ 112 | g++ \ 113 | gcc-multilib \ 114 | wget \ 115 | liblzma-dev \ 116 | zlib1g-dev 117 | ``` 118 | 119 | > You don't have to install them all if don't plan to do something big. Choose 120 | > only the packages You will need. 121 | 122 | For `flashrom` compilation these packages are necessary: 123 | 124 | ``` 125 | apt-get install -y libpci-dev libusb-dev \ 126 | libusb-1.0-0-dev libftdi-dev 127 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pxe-server 2 | ========== 3 | 4 | This repository contains PXE server that should help in installing, testing and 5 | developing operating systems and firmware for PXE-capable platforms. 6 | 7 | It was inspired by effort required to test PC Engines apu2 platform. 8 | 9 | 10 | Usage 11 | ----- 12 | 13 | ``` 14 | git clone https://github.com/3mdeb/pxe-server.git 15 | cd pxe-server 16 | NFS_SRV_IP= ./init.sh 17 | ``` 18 | 19 | `init.sh` downloads all necessary files, OS images, PXE and extracts them in 20 | proper directories. 21 | 22 | > `init.sh` script uses our netboot repository by default. It is the repository it 23 | > should be paired with. 24 | 25 | Please note that `init.sh` also download prepared Debian boot images. In root 26 | directory of those images you can find `CHANGELOG` document which briefly 27 | describe modifications. 28 | 29 | APU2 development and testing 30 | ---------------------------- 31 | 32 | ### Setting up docker container 33 | 34 | In order to set up isolated environment for pxe-server with nfs-server and 35 | http-boot, just run: 36 | 37 | ``` 38 | ./start.sh 39 | ``` 40 | 41 | This script builds a container and runs it with correct configuration 42 | nfs-kernel-server. 43 | 44 | `run.sh` is a script that runs at container startup, do not use it on Your host 45 | PC. 46 | 47 | ## Chainloading over HTTP 48 | 49 | In some situation it may happen that TFTP server may be unreliable. There are 50 | known network configurations where routers filter tftp traffic. Because of that 51 | we decided to switch over to HTTP. 52 | 53 | Boot to iPXE and type: 54 | 55 | ``` 56 | iPXE> ifconf net0 57 | iPXE> dhcp net0 58 | iPXE> chain http://:8000/menu.ipxe 59 | ``` 60 | 61 | Of course please replace `` with address provided during 62 | initialization (`NFS_SRV_IP`). 63 | 64 | ### Select options 65 | 66 | Currently supported options are: 67 | 68 | 1. `Debian stable netboot` - it is a Debian Stretch rootfs served over nfs with custom 69 | kernel 70 | 2. `Voyage netinst` - a Voyage Linux network installation image 71 | 3. `Debian stable netinst` - runs a Debian stable amd64 network installation from external repository 72 | 4. `Debian testing netinst` - runs a Debian testing amd64 network installation from external repository 73 | 74 | The credentials for Debian stable netboot are as follows: 75 | login: root 76 | password: debian 77 | 78 | Those credentials are visible during boot: 79 | 80 | ``` 81 | Debian GNU/Linux 9 apu2 ttyS0 [root:debian] 82 | 83 | apu2 login: 84 | ``` 85 | 86 | ## Robot Framework 87 | 88 | Some automation of above process has been prepared. Relevant source code can be 89 | found [here](https://github.com/pcengines/apu-test-suite) 90 | 91 | 92 | ## Issues 93 | 94 | I have encountered issues with network interface configuration. The 95 | configuration is retrieved from DHCP 3 times: 96 | 97 | 1. In iPXEshell 98 | 2. Before nfs mount during boot time 99 | 3. At system startup (defined in /etc/network/interfaces) 100 | 101 | > 1 and 2 are necessary, 3 is only needed to get internet connection on booted 102 | system. 103 | 104 | Requesting configuration that many times makes a little mess, so as a temporary 105 | workaround add a static IP for the `net0/eth0` interface on Your DHCP server. 106 | The IP address requested will remain the same and so the problems will be gone 107 | too. 108 | 109 | -------------------------------------------------------------------------------- /docs/ipxe_home_server.md: -------------------------------------------------------------------------------- 1 | # iPXE on home server # 2 | 3 | This guide is about setting up an iPXE-tftp server on home server machine. 4 | 5 | ## Installation ## 6 | 7 | 1. Server should be running inside a Docker container therefore docker is 8 | required. To install docker go [here](https://docs.docker.com/engine/installation/). 9 | 10 | 2. To build environment and install ipxe server go to [this](https://github.com/3mdeb/pxe-server) 11 | address and follow instructions. According to FHS, pxe-server should be cloned 12 | to `/opt/pxe-tftpd/`. Note that this catalog is used later. It can be changed 13 | but this requires changes in `ipxe-tftp.service` (if used). 14 | 15 | 3. Script `./init.sh` builds environment for Debian i386 installation. Note that 16 | it creates two catalogs 'nfs' and 'netboot', then Debian i386 netboot installer 17 | is downloaded and unpacked inside 'netboot'. 18 | 19 | 4. Inside container: 20 | - To run tftp server use `sudo service tftpd-hpa start`. 21 | 22 | To autostart tftp daemon Dockerfile should be modified with line at the 23 | bottom:`ENTRYPOINT service tftpd-hpa start && bash` 24 | 25 | ## Autostart container ## 26 | 27 | There are some ways to autostart container. We will use systemd. Configuration 28 | file is provided: `ipxe-tftp.service` in current directory. 29 | 30 | 1. Copy configuration file to proper destination: 31 | `sudo cp ipxe-tftp.service /lib/systemd/system/` 32 | 33 | 2. Reload daemon: 34 | `sudo systemctl daemon-reload` 35 | 36 | 3. Start created service: 37 | `sudo systemctl start ipxe-tftp.service` 38 | 39 | 4. Check if our container is up: 40 | `sudo systemctl status ipxe-tftp.service` 41 | `sudo docker ps` 42 | 43 | 5. Set autostart: 44 | `sudo systemctl enable ipxe-tftp.service` 45 | 46 | ## Client usage example ## 47 | ``` 48 | iPXE> dhcp net0 49 | iPXE> set filename pxelinux.0 50 | iPXE> set next-server 51 | iPXE> chain tftp://${next-server}/${filename} 52 | ``` 53 | Note, that shortcut chain `tftp:///pxelinux.0` will not work and 54 | will cause system hanging. 55 | 56 | ## Multiple systems booting ## 57 | 58 | Booting multiple systems requires proper boot menu configuration and can be a 59 | little tricky. There are several ways to do it but, as mentioned in OpenBSD doc, 60 | we are using Debian boot menu and adding desired options. This example covers 61 | multiple versions of Debian but it can be easily done for other systems using 62 | the same pattern. 63 | 64 | #### Folder structure #### 65 | 66 | In default (after using `init.sh` script) folder structure is a result of 67 | unpacking debian netboot tarball in netboot folder: 68 | 69 | ``` 70 | . 71 | ├── debian-installer 72 | │   └── i386 73 | ├── ldlinux.c32 -> debian-installer/i386/boot-screens/ldlinux.c32 74 | ├── pxelinux.0 -> debian-installer/i386/pxelinux.0 75 | ├── pxelinux.cfg -> debian-installer/i386/pxelinux.cfg 76 | └── version.info 77 | ``` 78 | For multiple systems following folder structure is proposed: 79 | ``` 80 | . 81 | ├── debian-installer 82 | │   ├── stable 83 | │   │   ├── amd64 84 | │   │   └── i386 85 | │   └── unstable 86 | │   ├── amd64 87 | │   └── i386 88 | ├── ldlinux.c32 -> debian-installer/stable/amd64/boot-screens/ldlinux.c32 89 | ├── pxelinux.0 -> debian-installer/stable/amd64/pxelinux.0 90 | ├── pxelinux.cfg -> debian-installer/stable/amd64/pxelinux.cfg/ 91 | └── version.info 92 | ``` 93 | Note, that after making changes symbolic links will be broken and will 94 | need refreshing. 95 | 96 | #### Boot menu config #### 97 | 98 | Two files needs to be edited by modifying file paths inside: 99 | - `debian-installer/stable/i386/pxelinux.cfg/default` 100 | - `debian-installer/stable/i386/boot-screens/menu.cfg` 101 | 102 | Then we need to add additional options in menu: 103 | - `debian-installer/stable/i386/boot-screens/txt.cfg` 104 | 105 | Pattern: 106 | 107 | ``` 108 | label install 109 | menu label ^Install 110 | kernel 111 | append vga=788 initrd= 112 | ``` 113 | 114 | During the modification of `txt.cfg` it is possible to modify kernel booting 115 | parameters. To make installer available on serial console add: 116 | `console=ttyS0,115200n8` 117 | 118 | ## Known issues ## 119 | 120 | #### Network jailing #### 121 | 122 | In default, Docker container cannot be accessed from outside world. To overcome 123 | this problem: `--net=host` parameter has been used. 124 | 125 | There surely are more sophisticated methods. To read more go to [docker site](https://docs.docker.com/engine/userguide/networking/default_network/binding/). 126 | 127 | #### TFTP vulnerability #### 128 | 129 | TFTP protocol is based on UDP therefore it very much relies on link stability. 130 | Transmission can be easily disrupted which immediately stops booting process. 131 | Best practice is to connect ipxe client and server to one node (router). 132 | --------------------------------------------------------------------------------