ODROID-C4 Hardware Platform¶
📟 Hardware¶
ODROID-C4 is a new generation single board computer that is more energy efficient and faster performing than ODROID-C2 which was introduced over four years ago as the world’s first affordable ARM 64bit computer.
The main CPU of the ODROID-C4 is built with a quad-core Cortex-A55 cluster with a new generation Mali-G31 GPU. The A55 cores run at 2.0Ghz without thermal throttling using the stock heat sink allowing a robust and quiet computer. The CPU multi-core performance is around 40% faster, and the system DRAM performance is 50% faster than the ODROID-C2.
| Type | Description |
|---|---|
| Processor | Amlogic S905X3 SoC - 4 x ARM® Cortex®-A55 2.0GHz |
| GPU | 4 x ARM® Mali-G31™ 650MHz |
| RAM | 4GB 32bit DDR4 |
| Flash Storage | Micro-SD UHS-I SDR104 or eMMC storage option |
| USB3.0 Host | 4 Ports |
| Networking | Gigabit Ethernet (10/100/1000 Mbit/s) |
| Expansion I/O | 40pin GPIOs + 7pin I2S |
| Power | 12V/2A - Stress: 3.64W - Idle: 0.18W |
| Dimensions | 85 x 56mm |
| Weight | 59g |
Tip
This website is hosted on the ODROID-C4 configured with a debian operating system.
The following documentation has been made with MkDocs.
GPIO

💿 Installing Debian¶

armbian is a free and open-source operating-system based on the debian linux system.
An operating-system is the set of basic programs and utilities that make a computer run.
Using armbian/debian on the odroid allows the access of thousands of cross-compiled packages for arm device.
A ssh server is installed by default in this release.
🛠️ Installation¶
Download Bullseye image
The first operation consists on downloading the armbian linux image and flashing it on eMMC.
Download the Bullseye linux image for ODROID-C4 https://www.armbian.com/odroid-c4.
Flash Bullseye image using etcher
Flash the Armbian_21.08.1_Odroidc4_bullseye_current_5.10.60.img.xz on the eMMC using etcher.

🔐 Login¶

Login on armbian
Login using ssh with user: root, password: 1234 on ip: 192.168.1.20:
ssh root@192.168.1.20
Add debian user account to sudo group if necessary
During the first connection on the odroid system a new user account will be created.
If this is not the case you will have to manually create a user account:
# add new user: debian
apt-get install sudo
sudo adduser debian
sudo usermod -aG sudo debian
⚙️ System Configuration¶
Configure Environment
To setup debian for the ODROID-C4, follow those steps.
# fix upgrade issue
sudo sed -i 's/bullseye-desktop//g' /etc/apt/sources.list.d/armbian.list
# update system
sudo apt-get update
sudo apt-get upgrade
# update linux kernel
sudo apt-get dist-upgrade
sudo apt-get autoremove --purge
sudo apt-get clean
# disable armbian logging services
sudo systemctl disable armbian-ramlog.service
sudo systemctl disable armbian-zram-config.service
sudo sed -i 's/ENABLED=true/ENABLED=false/g' /etc/default/armbian-ramlog
sudo rm /etc/cron.d/armbian-truncate-logs
sudo rm /etc/cron.daily/armbian-ram-logging
# force cpufreq governor to schedutil
sudo sed -i 's/ENABLE=.*/ENABLE=true/g' /etc/default/cpufrequtils
sudo sed -i 's/GOVERNOR=.*/GOVERNOR=schedutil/g' /etc/default/cpufrequtils
# configure sshd daemon
sudo sed -i 's/#*MaxAuthTries [0-9].*/MaxAuthTries 3/g' /etc/ssh/sshd_config # limit failed authentication attempts per connection to 3
sudo sed -i 's/#LoginGraceTime 2m/LoginGraceTime 20s/g' /etc/ssh/sshd_config # allow only 20 seconds for a client to authenticate before disconnecting
sudo sed -i 's/X11Forwarding yes/X11Forwarding no/g' /etc/ssh/sshd_config # disable X11 forwarding to reduce attack surface
sudo systemctl reload ssh
# reboot
sync
sudo reboot now
Install telnet
To access the debian board in case sshd crash, telnetd will be installed.
# install telnet daemon
sudo apt-get install telnetd
# allow all connections from same subnet
echo 'telnetd: 192.168.1.0/255.255.255.0' | sudo tee -a /etc/hosts.allow
Tip
To access the odroid device via telnet: telnet ipv4_addr.
Activate DHCP
# activate DHCP
sudo tee /etc/network/interfaces > /dev/null <<EOF
source /etc/network/interfaces.d/*
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
allow-hotplug eth0
iface eth0 inet dhcp
EOF
Install SSH Keys
The following procedure install ssh security keys from the client (ex: Windows) to the remote computer in order to login without the need of password.
# on desktop
ssh-keygen -t rsa
Define host/port/user parameters
REMOTE_HOST="host" # ex: git.example.com
REMOTE_PORT="port" # ex: 443
REMOTE_USER="user" # ex: git
$env:REMOTE_HOST="host" # ex: git.example.com
$env:REMOTE_PORT="port" # ex: 443
$env:REMOTE_USER="user" # ex: git
Append local public key to remote computer
# verify both vars are set; otherwise print an error and exit
[ -n "$REMOTE_HOST" ] && [ -n "$REMOTE_PORT" ] && [ -n "$REMOTE_USER" ] || { echo "Error: set REMOTE_HOST, REMOTE_PORT, REMOTE_USER"; return 1 2>/dev/null || exit 1; }
# append local public key to remote authorized_keys (preserves existing entries)
ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
cat "$HOME/.ssh/id_rsa.pub" | ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "cat >> ~/.ssh/authorized_keys"
ssh -p "$REMOTE_PORT" "$REMOTE_USER@$REMOTE_HOST" "chmod 600 ~/.ssh/authorized_keys"
# verify both vars are set; otherwise print an error and exit
if (-not $env:REMOTE_HOST -or -not $env:REMOTE_PORT -or -not $env:REMOTE_USER) { Write-Error "Error: set REMOTE_HOST, REMOTE_PORT, REMOTE_USER"; return }
# append local public key to remote authorized_keys (preserves existing entries)
ssh -p $env:REMOTE_PORT "$($env:REMOTE_USER)@$($env:REMOTE_HOST)" "mkdir -p ~/.ssh && chmod 700 ~/.ssh"
Get-Content "$env:USERPROFILE\.ssh\id_rsa.pub" | ssh -p $env:REMOTE_PORT "$($env:REMOTE_USER)@$($env:REMOTE_HOST)" "cat >> ~/.ssh/authorized_keys"
ssh -p $env:REMOTE_PORT "$($env:REMOTE_USER)@$($env:REMOTE_HOST)" "chmod 600 ~/.ssh/authorized_keys"
Info
It's now possible to login without entering password using ssh -p <port> <user>@<host>
Configure Firewall (ufw)
The goal of a firewall on Linux is to control and manage network traffic based on predetermined security rules. It acts as a barrier between a trusted internal network and untrusted external networks, such as the internet.
Install ufw Firewall
ufw is a straightforward command-line tool that manages iptables configurations, the core firewall infrastructure of Linux.
Docker also interacts with these iptables rules, often overriding ufw settings and enforcing its own rules.
# install ufw firewall
sudo apt-get install ufw
Docker and ufw
If you are using the Uncomplicated Firewall (ufw) alongside Docker, you may encounter situations where your Dockerized web applications remain accessible from the internet despite ufw settings. This issue arises because both ufw and Docker manipulate the same underlying iptables configurations, potentially leading to conflicting rules that expose Docker containers regardless of ufw's active state.
A recommended approach involves prioritizing the ufw rules over those of Docker. This can be accomplished using the following command lines:
# force ufw rules before docker's
sudo tee -a /etc/ufw/after.rules > /dev/null <<EOF
# Put Docker behind UFW
*filter
:DOCKER-USER - [0:0]
:ufw-user-input - [0:0]
-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i eth0 -j ufw-user-input
-A DOCKER-USER -i eth0 -j DROP
COMMIT
EOF
# restart and reload ufw rules
sudo ufw reload
Configuring ufw Firewall
The following commands will configure the Linux firewall.
# clean ufw rules
sudo ufw reset # reset all firewall settings
sudo ufw disable # disable firewall before blocking connexions => otherwise ssh will stop
sudo ufw default deny incoming # deny all incoming connexions by default
# exposes ports of containers and hosts for specific port
sudo ufw allow 80/tcp # allow http requests
sudo ufw allow 443/tcp # allow https requests
sudo ufw allow 445/tcp # allow samba requests
sudo ufw allow 22/tcp # allow ssh requests
# activate the new ufw rules
sudo ufw enable # enable the firewall with new rules
sudo ufw status verbose # show firewall status
Protect Against Brute-Force Attacks (fail2ban)
Fail2ban is a lightweight intrusion prevention tool.
It monitors service logs (like SSH) and automatically bans IP addresses that show malicious signs, such as too many failed login attempts in a short time.
This prevents brute-force attacks from overwhelming your server with endless authentication attempts.
Install fail2ban
# install fail2ban
sudo apt-get install -y fail2ban
Configure fail2ban for SSH
# configure global fail2ban settings (applies to all jails)
sudo tee /etc/fail2ban/fail2ban.local > /dev/null <<EOF
[DEFAULT]
allowipv6 = auto
EOF
# configure a dedicated jail for SSH protection
sudo tee /etc/fail2ban/jail.local > /dev/null <<EOF
[sshd]
enabled = true
port = 22
logpath = /var/log/auth.log
maxretry = 3
findtime = 10m
bantime = 24h
ignoreip = 127.0.0.1/8 ::1 192.168.0.0/16 10.0.0.0/8
EOF
# enable and start the service
sudo systemctl enable --now fail2ban
# reload after changes to jail.local
sudo systemctl restart fail2ban
# check that the service is running
sudo systemctl status fail2ban
Verify and monitor bans
# show current status of the SSH jail (number of failures, active bans, etc.)
sudo fail2ban-client status sshd
# monitor authentication log for incoming attempts
sudo tail -n 25 /var/log/auth.log
# monitor fail2ban's own actions (bans, unbans, warnings)
sudo tail -n 25 /var/log/fail2ban.log
💾 Configure SSD¶
Detect SSD device
# detect device name
lsblk
*NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 465.8G 0 disk
└─sda1 8:1 0 465.8G 0 part
mmcblk1 179:0 0 116.5G 0 disk
└─mmcblk1p1 179:1 0 115.3G 0 part /
Tip
The SSD device name here is: /dev/sda.
Format SSD in ext4
# install parted
sudo apt-get install parted
# create a GPT partition table
sudo parted <device-name> --script -- mklabel gpt
# create ext4 partition that takes the whole space
sudo parted <device-name> --script -- mkpart primary ext4 0% 100%
# format partition to ext4
sudo mkfs.ext4 -F <device-name>
sync
# verify the partition table
sudo parted <device-name> --script print
Tip
Replace <device-name> with your sd-card device name (ex: /dev/sda).
Mount SSD automatically
The following steps describe how to mount automatically the SSD using its UUID.
# install setfacl/getfacl packages
sudo apt-get install acl
# get blkid and save UUID
sudo blkid
# mount automatically
sudo mkdir -p <ssd_dir>
echo 'UUID=<UUID-VALUE> <ssd_dir> ext4 defaults,noatime,nodiratime,commit=60,errors=remount-ro,acl,user,exec 0 2' | sudo tee -a /etc/fstab > /dev/null
Replace the following fields
| Field | Description |
|---|---|
<ssd_dir> |
the directory where the SSD will be mounted (ex: /media/ssd) |
<UUID-VALUE> |
the UUID value (without quotes) of the SSD read by blkid program |
Configure TRIM for SSD/eMMC Devices
eMMC and SSD storage have a limited number of write cycles. Without proper maintenance, excessive write operations can shorten their lifespan, potentially leading to data corruption or device failure. Enabling TRIM helps the operating system inform the storage device which blocks of data are no longer in use, allowing it to manage wear more efficiently and extend the device’s durability.
# configure fstrim for all devices that support it
sudo tee /etc/cron.weekly/fstrim > /dev/null <<EOF
#!/bin/sh
/sbin/fstrim --all || true
exit 0
EOF
sudo chmod +x /etc/cron.weekly/fstrim
Optimize Filesystem and Memory Settings for SSD/eMMC Devices
These system tweaks are recommended to reduce unnecessary write operations on eMMC or SSD devices, extending their lifespan and improving performance:
- Disable Swap: Sets
vm.swappiness=0to avoid using swap space on eMMC. - Suppress Access Time Updates: Adds
nodiratimeto reduce metadata writes for directory access. - Extend Commit Interval: Sets
commit=60to flush metadata changes every 60 seconds instead of more frequently.
# minimize swapping but avoid sudden OOM when RAM is full (1 instead of 0)
echo "vm.swappiness=1" | sudo tee /etc/sysctl.d/99-swappiness.conf > /dev/null
# always allow memory allocations (risk: possible sudden OOM)
echo "vm.overcommit_memory=1" | sudo tee /etc/sysctl.d/99-overcommit.conf > /dev/null
# reload sysctl configs
sudo sysctl --system
# disable access time on files and directories
sudo sed -i 's/noatime,commit/noatime,nodiratime,commit/g' /etc/fstab
# reduce frequency of metadata writing to 60s
sudo sed -i 's/,commit=[0-9]\+/,commit=60/g' /etc/fstab