Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
debian/
final/
authorized_keys.gunet
*.env
85 changes: 55 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
# JeOS

GUNet Just enough OS (based on Debian)

## Docker

The final ISO image will be in the folder `/var/jeos/final`. We must volume mount it in order to have it available after
the Docker container has finished.
The recommened way to run the container is:
`docker run --rm -v ${PWD}/final:/var/jeos/final --privileged ghcr.io/gunet/jeos-builder:<version>`
The recommended way to run the container is:

```bash
docker run --rm -v ${PWD}/final:/var/jeos/final --privileged ghcr.io/gunet/jeos-builder:<version>
```

### Environment variables

The following environment variables are available. For network configuration, the general path is to use DHCP provided ones and *only* if these are not available, then use the ones in environment variables (if they are provided):

* `NET_IP`: The static IP in CIDR form (ie `192.168.2.1/24`).
* `NET_GATEWAY`: The gateway IP. Only if IP has already been passed.
* `NET_NAMESERVERS`: Nameserver IPs to use, separated by space (ie `8.8.8.8 4.4.4.4`)
* `NET_NAMESERVERS`: Name Server IPs to use, separated by space (ie `8.8.8.8 4.4.4.4`)
* `NET_HOSTNAME`: The hostname to set (ie `sso.gunet.gr`)
* `NET_DOMAIN`: The domain to set (ie `gunet.gr`)
* `NET_STATIC`: If set to `yes` then we only perform static network configuration and **all** the above variables **must** be set
* `ROOT_PASSWORD`: The (plaintext) root password

The `NET_HOSTNAME` and `NET_DOMAIN` can be helpful even in a DHCP configuration in order to *avoid* the installer asking for the corresponing values.
The `NET_HOSTNAME` and `NET_DOMAIN` can be helpful even in a DHCP configuration in order to *avoid* the installer asking for the corresponding values.

If no environment variables are passed then the installer will just ask more questions. The purpose of the environment variables is mainly to avoid asking questions and making the installation completely non-interactive (for instace in order to use the produced ISO as input to a [packer](https://github.com/gunet/packer) template).
If no environment variables are passed then the installer will just ask more questions. The purpose of the environment variables is mainly to avoid asking questions and making the installation completely non-interactive (for instance in order to use the produced ISO as input to a [packer](https://github.com/gunet/packer) template).

Sample full `vm.env` file (include with `--env-file vm.env` option in `docker run`):
```

```bash
NET_IP=123.123.123.123/25
NET_GATEWAY=123.123.123.1
NET_NAMESERVERS=8.8.8.8
Expand All @@ -33,66 +41,83 @@ ROOT_PASSWORD=password
```

### ssh keys

* By default root is allowed public key ssh access in the resulting VM
* If you volume mount an `authorized_keys` file under `gunet/` folder then it will be used in the resulting ISO: `-v ${PWD}/authorized_keys:/var/jeos/gunet/authorized_keys`

### Special Notes

* The ssh server will listen on port `65432` instead of the default `22`

### Building

* Default `ARG` values:
- ARG DEBIAN_VERSION=11.8.0
- ARG DEBIAN_REPO=https://cdimage.debian.org/mirror/cdimage/archive/${DEBIAN_VERSION}/amd64/iso-cd
- ARG DEBIAN_ISO=debian-${DEBIAN_VERSION}-amd64-netinst.iso
* ARG DEBIAN_VERSION=11.8.0
* ARG DEBIAN_REPO=<https://cdimage.debian.org/mirror/cdimage/archive/${DEBIAN_VERSION}/amd64/iso-cd>
* ARG DEBIAN_ISO=debian-${DEBIAN_VERSION}-amd64-netinst.iso
* Simple build: `docker build -t ghcr.io/gunet/jeos-builder .`
* Build for another debian version: `docker build --build-arg DEBIAN_VERSION=<version> -t ghcr.io/gunet/jeos-builder .`

### Available versions

* Debian 11: `latest`: `11.8.0`
* Debian 12: `12.2.0`

## HTTP Server
You can use the `httpd` Docker image to run an httpd server to temporarilly expose the ISO image in order to use in web installations (will listen on port `80`):

You can use the `httpd` Docker image to run an httpd server to temporarily expose the ISO image in order to use in web installations (will listen on port `80`):
`docker run --rm -p 80:80 --name jeos-web -v ${PWD}/final:/usr/local/apache2/htdocs/ httpd:2.4`

## Mount

If you want to mount the resulting ISO image locally:

* `mkdir /mnt/iso`
* `mount -o loop final/gunet-jeos-debian-<version>.iso /mnt/iso`
* To unmount: `umount /mnt/iso`

## Size

* Docker image:
- Debian 11: `510 MB`
- Debian 12: `760 MB`
* Debian 11: `510 MB`
* Debian 12: `760 MB`
* JeOS ISO CD:
- Debian 11: `450 MB`
- Debian 12: `750 MB`
* Debian 11: `450 MB`
* Debian 12: `750 MB`
* JeOS installation:
- Debian 11: `1.2 GB`
- Debian 12: `1.8 GB`
* Debian 11: `1.2 GB`
* Debian 12: `1.8 GB`

## Notes for repo files

### Run
In order to produce a Just Enough Operating System iso image, we need to run the script __mkiso.sh__ as follows:
`sudo ./mkiso.sh`

In order to produce a Just Enough Operating System iso image, we need to run the script [mkiso.sh](mkiso.sh) as follows:

```bash
sudo ./mkiso.sh
```

It uses the following two environment variables:

* `DEBIAN_ISO` to find the Debian ISO in `$(pwd)/debian/${DEBIAN_ISO}`
* `DEBIAN_VERSION` (if available) to use it in the name of the produced ISO file. The default name is `$(pwd)/final/gunet-jeos-debian.iso` and if the `DEBIAN_VERSION` variable is available `$(pwd)/final/gunet-jeos-debian-${DEBIAN_VERSION}.iso`

The `DEBIAN_ISO` file is a Debian ISO file from the Debian project. An archive of ISO images for previous
Debian versions can be found [here](https://cdimage.debian.org/mirror/cdimage/archive/)

### Configuration
The produced .iso file installs a Debian OS, by requesting only the root password and the network configuration paramenters, in case DHCP fails, during the installation if the necessary environment variables are not available. All the configuration must be located into _gunet/_ folder. In the current configuration, _gunet/_ folder contains the follwing:
* <ins>_preseed.cfg_</ins>: This file contains all the configuration of d-i installer that automates the installation procedure. The parameters are set to produce an as minimal as possible installation. During the _late_command_ step, we add further configuration and run scripts that we want to include in the installation procedure.
* <ins>_00norecommends_</ins>: This file is copied into the _/etc/apt/apt.conf.d/_ folder by the late_command of d-i installer and prevents the installation of suggested and recommended packages during package installation via ```apt```.
* <ins>_disableipv6.conf_</ins>: This file is copied into the _/etc/sysctl.d/_ folder by the late_command of d-i installer and disables ipv6 network configuration.
* <ins>_locale_</ins>: This file is copied into the _/etc/default/_ folder by the late_command of d-i installer and sets up the locale configuration.
* <ins>_sources.list_</ins>: This file is copied into the _/etc/apt/_ folder by the late_command of d-i installer and defines the repos from which the packages will be installed via ```apt```.
* <ins>_internal_custom_script.sh_</ins>: This script is executed by the late_command of d-i installer and adds or removes packages to/from the initial installation. Any package addition or removal should be included here.
* <ins>_custom_script.sh_</ins>: This script is executed by the late_command of d-i installer and configures services(i.e. the port of the ssh server) and removes files or folders. Any configuration of system services should be included here.
- In our case we change the ssh port from the default `22` to `65432`.
* <ins>_install_docker_</ins>: This script installs docker.

***Note***: For any further addition to the above, the new configuration file or script must be included in the _gunet/_ file and the respective changes in the late_command of the preseed.cfg file must be performed.

The produced .iso file installs a Debian OS, by requesting only the root password and the network configuration parameters, in case DHCP fails, during the installation if the necessary environment variables are not available. All the configuration must be located into [gunet/](gunet/) folder. In the current configuration, [gunet/](gunet/) folder contains the following:

* [preseed.cfg](gunet/preseed.cfg): This file contains all the configuration of d-i installer that automates the installation procedure. The parameters are set to produce an as minimal as possible installation. During the *late_command* step, we add further configuration and run scripts that we want to include in the installation procedure.
* [00norecommends](gunet/00norecommends): This file is copied into the */etc/apt/apt.conf.d/* folder by the late_command of d-i installer and prevents the installation of suggested and recommended packages during package installation via ```apt```.
* [disableipv6.conf](gunet/disableipv6.conf): This file is copied into the */etc/sysctl.d/* folder by the late_command of d-i installer and disables ipv6 network configuration.
* [locale](gunet/locale): This file is copied into the */etc/default/* folder by the late_command of d-i installer and sets up the locale configuration.
* [sources.list](gunet/sources.list): This file is copied into the */etc/apt/* folder by the late_command of d-i installer and defines the repos from which the packages will be installed via ```apt```.
* [internal_custom_script.sh](gunet/internal_custom_script.sh): This script is executed by the late_command of d-i installer and adds or removes packages to/from the initial installation. Any package addition or removal should be included here.
* [custom_script.sh](gunet/custom_script.sh): This script is executed by the late_command of d-i installer and configures services(i.e. the port of the ssh server) and removes files or folders. Any configuration of system services should be included here.
* In our case we change the ssh port from the default `22` to `65432`.
* [install_docker](gunet/install_docker): This script installs docker.

***Note***: For any further addition to the above, the new configuration file or script must be included in the [gunet/](gunet/) file and the respective changes in the late_command of the preseed.cfg file must be performed.
23 changes: 23 additions & 0 deletions cspell-words.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
00norecommends
cdrom
cpio
disableipv6
emul
genisoimage
htdocs
irmod
ISODIR*
ISOFILE*
isofiles # see https://cspell.org/docs/dictionaries-custom/
isohybrid
isolinux
gunet
jeos
mkiso
mknod
netinst
newc
notset
preseed
syslinux
xorriso
17 changes: 17 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Enable your dictionary by adding it to the list of `dictionaries`
"dictionaries": ["custom-words"],

// Tell CSpell about your dictionary
"dictionaryDefinitions": [
{
// The name of the dictionary is used to look it up.
"name": "custom-words",
// Path to the custom word file. Relative to this `cspell.json` file.
"path": "./cspell-words.txt",
// Some editor extensions will use `addWords` for adding words to your
// personal dictionary.
"addWords": true
}
]
}
4 changes: 2 additions & 2 deletions gunet/install_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ echo "Install git.."
apt -qqy install git
echo "Add Docker repository GPG key.."
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
echo "Add Docker reposity.."
echo "Add Docker repository.."
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
echo "apt update (second time).."
apt update
Expand All @@ -18,7 +18,7 @@ echo "Show docker status.."
systemctl status docker
echo "Running docker version.."
docker version
echo "Dowloading docker-compose.."
echo "Downloading docker-compose.."
curl -fsSL https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64 >/usr/local/bin/docker-compose
chmod 0755 /usr/local/bin/docker-compose
echo "Checking docker-compose version"
Expand Down
1 change: 1 addition & 0 deletions gunet/preseed.cfg
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# cSpell: disable
#### Contents of the preconfiguration file (bullseye)
### Localization
d-i debian-installer/language string en
Expand Down
65 changes: 33 additions & 32 deletions mkiso.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash
# Include the helper functions
source $(dirname "$0")/helper_functions.sh
# shellcheck disable=SC1091
source "$(dirname "$0")"/helper_functions.sh

set -e
set -u
Expand All @@ -23,15 +24,15 @@ if [[ ! -v DEBIAN_ISO || ! -v DEBIAN_VERSION ]]; then
exit 1
fi

DEBIAN_MAJOR=$(echo -n ${DEBIAN_VERSION}|cut -d. -f 1)
DEBIAN_MAJOR=${DEBIAN_VERSION%%.*}
echo "Debian major version is ${DEBIAN_MAJOR}.."
if [[ ${DEBIAN_MAJOR} != "11" && ${DEBIAN_MAJOR} != "12" ]]; then
echo "Debian major version not supported!"
exit 1
fi
if [[ ${DEBIAN_MAJOR} == "12" ]]; then
echo "Updating sources.list for Debian 12.."
sed -i'' 's/bullseye/bookworm/g' ${PRESEED_DIR}/sources.list
sed -i'' 's/bullseye/bookworm/g' "${PRESEED_DIR}"/sources.list
fi

ISOFILE=${PROJECT_DIR}/debian/${DEBIAN_ISO}
Expand All @@ -45,20 +46,20 @@ if [[ ${NET_STATIC} == "yes" ]]; then
echo "Environment variable NET_STATIC is yes but some NET_* variables are not set!"
exit 1
fi
sed -i'' -e "s/^#STATIC#//g" ${PRESEED_DIR}/preseed.cfg
sed -i'' -e "s/^#STATIC#//g" "${PRESEED_DIR}"/preseed.cfg
fi
if [[ ${NET_IP} != "notset" ]]; then
if [[ ${NET_GATEWAY} == "notset" || ${NET_NAMESERVERS} == "notset" ]]; then
echo "Environment variable NET_IP is set but NET_GATEWAY or NET_NAMESERVERS are not!"
exit 1
fi
NET_IP_PLAIN=$(cidr_ip ${NET_IP})
NET_PREFIX=$(cidr_prefix ${NET_IP})
NET_IP_PLAIN=$(cidr_ip "${NET_IP}")
NET_PREFIX=$(cidr_prefix "${NET_IP}")
if [[ ${NET_PREFIX} == "" ]]; then
echo "NET_IP should be of CIDR form"
exit 1
fi
NET_NETMASK=$(netmask_of_prefix ${NET_PREFIX})
NET_NETMASK=$(netmask_of_prefix "${NET_PREFIX}")

echo "Network configuration:"
echo "IP (CIDR): ${NET_IP}"
Expand All @@ -70,7 +71,7 @@ if [[ ${NET_IP} != "notset" ]]; then
echo "-------------------------------"

sed -i'' -e "s/^#NET#//g" -e "s/__IP__/${NET_IP_PLAIN}/" -e "s/__NETMASK__/${NET_NETMASK}/" \
-e "s/__GATEWAY__/${NET_GATEWAY}/" -e "s/__NAMESERVERS__/${NET_NAMESERVERS}/" ${PRESEED_DIR}/preseed.cfg
-e "s/__GATEWAY__/${NET_GATEWAY}/" -e "s/__NAMESERVERS__/${NET_NAMESERVERS}/" "${PRESEED_DIR}"/preseed.cfg
fi

if [[ ${NET_HOSTNAME} != "notset" ]]; then
Expand All @@ -84,70 +85,70 @@ if [[ ${NET_HOSTNAME} != "notset" ]]; then
echo "----------------------------"

sed -i'' -e "s/^#HOST#//g" \
-e "s/__HOSTNAME__/${NET_HOSTNAME}/" -e "s/__DOMAIN__/${NET_DOMAIN}/" ${PRESEED_DIR}/preseed.cfg
-e "s/__HOSTNAME__/${NET_HOSTNAME}/" -e "s/__DOMAIN__/${NET_DOMAIN}/" "${PRESEED_DIR}"/preseed.cfg
fi

if [[ ${ROOT_PASSWORD} != "notset" ]]; then
echo "Root passwd: ${ROOT_PASSWORD}"
sed -i'' -e "s/^#ROOT#//g" -e "s/__ROOT_PASSWORD__/${ROOT_PASSWORD}/" ${PRESEED_DIR}/preseed.cfg
sed -i'' -e "s/^#ROOT#//g" -e "s/__ROOT_PASSWORD__/${ROOT_PASSWORD}/" "${PRESEED_DIR}"/preseed.cfg
fi

sed -i "s/^M//" $PRESEED_DIR/custom_script.sh
sed -i "s/^M//" "$PRESEED_DIR"/custom_script.sh

if [[ ! -e /dev/loop0 ]]; then
echo 'Creating loop device..'
mknod -m 0660 /dev/loop0 b 7 0
fi
echo 'mounting ISO9660 filesystem...'
# source: http://wiki.debian.org/DebianInstaller/ed/EditIso
[ -d $ISODIR ] || mkdir -p $ISODIR
mount -o loop $ISOFILE $ISODIR
[ -d "$ISODIR" ] || mkdir -p "$ISODIR"
mount -o loop "$ISOFILE" "$ISODIR"

echo 'copying to writable dir...'
rm -rf $ISODIR_WRITE || true
[ -d $ISODIR_WRITE ] || mkdir -p $ISODIR_WRITE
rsync --info=progress2 -a -H --exclude=TRANS.TBL $ISODIR/ $ISODIR_WRITE
rm -rf "$ISODIR_WRITE" || true
[ -d "$ISODIR_WRITE" ] || mkdir -p "$ISODIR_WRITE"
rsync --info=progress2 -a -H --exclude=TRANS.TBL "$ISODIR"/ "$ISODIR_WRITE"
echo 'unmount iso dir'
umount $ISODIR
umount "$ISODIR"

echo 'correcting permissions...'
chmod 755 -R $ISODIR_WRITE
chmod 755 -R "$ISODIR_WRITE"

echo 'copying preseed files...'
cp gunet/isolinux.cfg $ISODIR_WRITE/isolinux/
cp -r $PRESEED_DIR/ $ISODIR_WRITE/
cp gunet/isolinux.cfg "$ISODIR_WRITE"/isolinux/
cp -r "$PRESEED_DIR"/ "$ISODIR_WRITE"/

echo 'edit isolinux/txt.cfg...'
sed 's/initrd.gz/initrd.gz file=\/cdrom\/gunet\/preseed.cfg/' -i $ISODIR_WRITE/isolinux/txt.cfg
sed 's/initrd.gz/initrd.gz file=\/cdrom\/gunet\/preseed.cfg/' -i "$ISODIR_WRITE"/isolinux/txt.cfg

echo 'creating initrd.gz..'
mkdir -p irmod
cd irmod
gzip -d < $ISODIR_WRITE/install.amd/initrd.gz | \
gzip -d < "$ISODIR_WRITE"/install.amd/initrd.gz | \
cpio --extract --make-directories --no-absolute-filenames
cp $PRESEED_DIR/preseed.cfg preseed.cfg
cp "$PRESEED_DIR"/preseed.cfg preseed.cfg
chown root:root preseed.cfg
chmod o+w $ISODIR_WRITE/install.amd/initrd.gz
chmod o+w "$ISODIR_WRITE"/install.amd/initrd.gz
find . | cpio -H newc --create | \
gzip -9 > $ISODIR_WRITE/install.amd/initrd.gz
chmod o-w $ISODIR_WRITE/install.amd/initrd.gz
gzip -9 > "$ISODIR_WRITE"/install.amd/initrd.gz
chmod o-w "$ISODIR_WRITE"/install.amd/initrd.gz
cd ../
rm -fr irmod/

echo 'fixing MD5 checksums...'
pushd $ISODIR_WRITE
md5sum $(find -type f) > md5sum.txt
pushd "$ISODIR_WRITE"
# shellcheck disable=SC2185
md5sum "$(find -type f)" > md5sum.txt
popd

echo 'making ISO...'
genisoimage -o $ISOFILE_FINAL \
genisoimage -o "$ISOFILE_FINAL" \
-r -J -no-emul-boot -boot-load-size 4 \
-boot-info-table -quiet \
-b isolinux/isolinux.bin \
-c isolinux/boot.cat $ISODIR_WRITE
-c isolinux/boot.cat "$ISODIR_WRITE"

isohybrid $ISOFILE_FINAL
isohybrid "$ISOFILE_FINAL"

rm -r isofiles*
echo 'finished'