“Raspberry Pi Doesn’t Gather Dust” Issue 1: Flashing Ubuntu Server 20.04, Binding a Public Domain Name, Providing HTTP Services to the Public Network, and SSH Login Service

zhaoolee
12 min readJan 18, 2024

--

Flashing Ubuntu Server 20.04, Binding a Public Domain Name, Providing HTTP Services to the Public Network, and SSH Login Service

A while ago, I bought an 8GB RAM Raspberry Pi 4B and used a USB wireless card to turn it into a soft router Turning a Raspberry Pi 4B with an OpenWrt image into a portable router that transforms standard wifi into magic wifi https://v2fy.com/p/2021-07-04-openwrt-4b-1625383754000/, but later I felt that it was a waste to use an 8GB Raspberry Pi as a soft router. So this time, I transformed the Raspberry Pi 4B into a server accessible from the public network to run some memory-intensive tasks.

The native system of Raspberry Pi is not as rich in packages as Ubuntu, so this time I chose to flash Ubuntu Server 20.04 LTS.

To ensure that the Raspberry Pi server at home could be accessed from the public network, I used the open-source FRP technology for internal network penetration to bind the domain name frp.v2fy.com to the Raspberry Pi. Accessing the HTTP service on the Raspberry Pi is as simple as visiting https://frp.v2fy.com. I also set up SSH mapping so that the Raspberry Pi can be accessed via SSH by connecting to port 6000 of frp.v2fy.com on the public network.

Principle Structure Diagram

About the Architecture of Internal Network Penetration

The browser’s default requests to a domain name point to ports 80 (HTTP) and 443 (HTTPS). I installed Nginx on the server end to occupy ports 80 and 443 for receiving HTTP and HTTPS requests. Then I wrote a set of rules to forward all requests targeting frp.v2fy.com to port 8080. Part of the HTTPS rules also include configuration of an HTTPS certificate for frp.v2fy.com (Nginx automatic certificate renewal operation can be referred to Independence! Using acme.sh to set up multiple HTTPS certificates for nginx to automatically update, and indefinitely renew HTTPS certificates https://v2fy.com/p/2021-06-27-nginx-https-1624774964000/), and port 8080 is occupied by the FRP server process frps, which sends requests to the FRP client frpc on the Raspberry Pi. FRPC then forwards requests from FRP to the http-server service running on 8080 on the Raspberry Pi; the SSH forwarding rule is similar to the HTTP forwarding rule, forwarding from the server's port 6000 to the Raspberry Pi's port 22.

Preliminary Preparation

  1. Indispensable Raspberry Pi kit: A Raspberry Pi (generations 2, 3, 4 are all suitable, I chose the Raspberry Pi 4B), Raspberry Pi power supply, an SD card of at least 8GB (16GB or above recommended).
  2. One good network cable with RJ45 connectors on both ends.
  3. A router with internet access and one available LAN port.
  4. A public network server with a fixed IP.
  5. One domain name.
  6. A 32GB or larger flash drive or high-capacity hard drive for expanding the server storage of the Raspberry Pi (optional).

The main content starts next…

Using SD Card Formatter to Format SD Card

SD Card Formatter download link https://www.sdcard.org/downloads/formatter/

Download Image

Download the image from https://ubuntu.com/download/raspberry-pi/thank-you?version=20.04.3&architecture=server-arm64+raspi

After clicking download now on the page, you will get the file ubuntu-20.04.3-preinstalled-server-arm64+raspi.img.

If your browser often fails to download, I suggest putting https://cdimage.ubuntu.com/releases/20.04.3/release/ubuntu-20.04.3-preinstalled-server-arm64+raspi.img.xz directly into Free Download Manager for downloading (Free Download Manager is a free download tool with free cloud acceleration download function, which is friendly for downloading overseas resources).

Using balenaEtcher to Flash the Image onto the SD Card

balenaEtcher download link https://www.balena.io/etcher/

Select the version you need to download according to your own system. If you’re on Windows, you can directly download the first one shown below.

Open Etcher, select the image file, choose the SD card, and start burning.

Here in the image it’s an 8GB SD card, but after a few failed attempts, I switched to a 16GB memory card and succeeded on the first try. If your burning fails repeatedly, I suggest trying with another SD card.

Insert the burned SD card into the Raspberry Pi, connect the Raspberry Pi to the router’s LAN port with a network cable, and power on the device.

Log into the Router Backend to Check the IP Assigned to the Raspberry Pi with Ubuntu Installed

Use SSH to Log in to the Raspberry Pi Server

The initial username and password are both ubuntu

After the first login, you will be forced to change the default password

Below, we use FRP internal network penetration to give the Raspberry Pi home server an external domain name

Internal Network Penetration: Server Setup

First, resolve your domain name to your server IP. The following steps will involve four server ports. To facilitate subsequent debugging runs, please open up the security policies for the following ports: 80 (for Nginx to receive HTTP requests), 443 (for Nginx to receive HTTPS requests), 6000 (for forwarding SSH services), and 8080 (for forwarding HTTP services) ports.

  • Configure Nginx to forward requests for the frp.v2fy.com domain to the 8080 port (before doing this step, please install Nginx according to your server’s operating system).

Set /etc/nginx/nginx.conf

#user  nginx;
worker_processes 1;
load_module /usr/lib64/nginx/modules/ngx_stream_module.so;
error_log  /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
stream {
map $ssl_preread_server_name $backend_name {
frp.v2fy.com frp_v2fy_com;
default web;
}
upstream frp_v2fy_com {
server 127.0.0.1:8081;
}
upstream web {
server 127.0.0.1:80;
}
server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}events {
worker_connections 1024;
}
http { include /etc/nginx/mime.types;
default_type application/octet-stream;
charset utf-8,gbk;
client_max_body_size 20m;
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
include /etc/nginx/conf.d/*.conf;
}

Create /etc/nginx/conf.d/frp.v2fy.com.conf, and write the following content into /etc/nginx/conf.d/frp.v2fy.com.conf (frp.v2fy.com is my domain, please replace it with your own domain name)

server {
server_name frp.v2fy.com;
listen 80;
    location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host:80;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 8081 ssl http2;
listen [::]:8081 ssl http2;
server_name frp.v2fy.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host:443;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
ssl_certificate "/etc/nginx/ssl/frp.v2fy.com/fullchain.cer";
ssl_certificate_key "/etc/nginx/ssl/frp.v2fy.com/frp.v2fy.com.key";
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;

Here is the translation of the provided Simplified Chinese Markdown documentation into English:

error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}

Restart nginx

nginx -t
nginx -s reload
  • Download frp and set up the server side

Frp software is divided into two parts, the client is frpc, the server is frps

cd /opt/
wget https://github.com/fatedier/frp/releases/download/v0.37.0/frp_0.37.0_linux_386.tar.gz
tar zxvf https://github.com/fatedier/frp/releases/download/v0.37.0/frp_0.37.0_linux_386.tar.gz

The server side’s frpc configuration file is frpc.ini

We need to modify the server-side configuration file frps.ini

[common]
bind_port = 7000
vhost_http_port = 8080

The above configuration means that the server side of frp named frps is running on port 7000, and it will forward requests targeting port 8080 to the client side;

  • Run the following command to start the frp server-side program
./frps -c frps.ini

frps runs in the foreground by default. If you want to run it as a background daemon process, you can use the Node.JS version of pm2 to keep it running.

  • Global installation method for pm2

First, install Node.js (please choose the appropriate Node.js version for your server OS), npm is installed by default with Node.js, then use npm to install pm2

npm i pm2 -g

pm2 is also simple to use

First, save ./frps -c frps.ini in a file named start_frps.sh

Then run

pm2 start /opt/frp_0.37.0_linux_arm64/start_frps.sh
pm2 save

This way you can run frps as a daemon process!

At this point, running pm2 list, you can see that the script is running...

Intranet Penetration: Client Setup

  • Execute the following command on the Raspberry Pi to download and extract frp
cd /opt/
sudo wget https://github.com/fatedier/frp/releases/download/v0.37.0/frp_0.37.0_linux_arm64.tar.gz
sudo tar zxvf frp_0.37.0_linux_arm64.tar.gz

Enter /opt/frp_0.37.0_linux_arm64

Backup the client-side frpc’s configuration file frpc.ini

sudo cp frpc.ini frpc.ini_backup
  • Edit frpc.ini
sudo vim frpc.ini
  • Insert the following configuration into frpc.ini
[common]
server_addr = (your server public IP)
server_port = 7000
[web]
type = http
local_port = 8080
custom_domains = frp.v2fy.com
  • Use a web browser to access the Raspberry Pi from the public network to verify if the intranet penetration is successful

Install Node.js (using the ARMv8 version of the stable Node.js)

(Updated January 14, 2024) If you want to install Node.js automatically, it’s recommended to use nvm https://github.com/nvm-sh/nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
nvm install 20.11.0
nvm alias default 20.11.0

After checking the wiki for Raspberry Pi CPU architecture, it seems that Raspberry Pi Series 3 and later versions are ARMv8 architecture

Use the ARMv8 version of the stable Node.js

cd /opt/
sudo wget https://nodejs.org/dist/v14.18.0/node-v14.18.0-linux-arm64.tar.xz
sudo tar xvf node-v14.18.0-linux-arm64.tar.xz
  • Add Node.js to system variables
sudo echo "export NODE_HOME=/opt/node-v14.18.0-linux-arm64" >> ~/.bashrc
sudo echo "export PATH=\$NODE_HOME/bin:\$PATH" >> ~/.bashrc
source ~/.bashrc
  • Install the http-server service
npm install http-server -g
  • Create a frp.v2fy.com folder in the /opt directory and create a hello.txt file
sudo mkdir /opt/frp.v2fy.com
sudo chmod 777 -R /opt/frp.v2fy.com
sudo touch /opt/frp.v2fy.com/hello.txt
sudo echo "Hello World! Success! " > /opt/frp.v2fy.com/hello.txt
  • Install pm2
npm install pm2 -g
  • Use pm2 to daemonize the http-server service
cd /opt/frp_0.37.0_linux_arm64 
sudo chmod 777 start_http_server.sh
sudo echo "http-server /opt/frp.v2fy.com -p 8080" > start_http_server.sh
pm2 start /opt/frp_0.37.0_linux_arm64/start_http_server.sh
pm2 save
  • Use pm2 to daemonize the frpc service
sudo touch /opt/frp_0.37.0_linux_arm64/start_frpc.sh
sudo chmod 777 /opt/frp_0.37.0_linux_arm64/start_frpc.sh
sudo echo "/opt/frp_0.37.0_linux_arm64/frpc -c /opt/frp_0.37.0_linux_arm64/frpc.ini" > /opt/frp_0.37.0_linux_arm64/start_frpc.sh
cd /opt/frp_0.37.0_linux_arm64/
pm2 start /opt/frp_0.37.0_linux_arm64/start_frpc.sh
pm2 save

Visit https://frp.v2fy.com

Here you can successfully access the HTTPS services of a home server via the public network.

Add SSH service

  • Add configuration
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

The configuration means to map the default ssh port 22 of the Raspberry Pi to port 6000 of frp.v2fy.com

  • Restart the frpc service
cd /opt/frp_0.37.0_linux_arm64
pm2 restart start_frpc.sh
  • Log in through the ssh of the public network frp.v2fy.com at port 6000
ssh ubuntu@frp.v2fy.com -p 6000

Thus, we can access our home Raspberry Pi server via ssh from any computer on the internet.

Set pm2 to start on boot, so that after an accidental power loss and restart of the Raspberry Pi, it can automatically start the intranet penetration

ubuntu@ubuntu:/opt/frp_0.37.0_linux_arm64$ pm2 startup
[PM2] Init System found: systemd
[PM2] To setup the Startup Script, copy/paste the following command:
sudo env PATH=$PATH:/opt/node-v14.18.0-linux-arm64/bin /opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
ubuntu@ubuntu:/opt/frp_0.37.0_linux_arm64$ sudo env PATH=$PATH:/opt/node-v14.18.0-linux-arm64/bin /opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu
[PM2] Init System found: systemd
Platform systemd
Template
[Unit]
Description=PM2 process manager
Documentation=https://pm2.keymetrics.io/
After=network.target
[Service]
Type=forking
User=ubuntu
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Environment=PATH=/opt/node-v14.18.0-linux-arm64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/opt/node-v14.18.0-linux-arm64/bin:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
Environment=PM2_HOME=/home/ubuntu/.pm2
PIDFile=/home/ubuntu/.pm2/pm2.pid
Restart=on-failure

ExecStart=/opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 resurrect ExecReload=/opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 reload all ExecStop=/opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 kill

[Install] WantedBy=multi-user.target

Target path: /etc/systemd/system/pm2-ubuntu.service Command list:

  • ‘systemctl enable pm2-ubuntu’ [PM2] Writing init configuration in /etc/systemd/system/pm2-ubuntu.service [PM2] Making script booting at startup… [PM2] [-] Executing: systemctl enable pm2-ubuntu… [PM2] [v] Command successfully executed. + — — — — — — — — — — — — — — — — — — — -+ [PM2] Freeze a process list on reboot via: $ pm2 save

[PM2] Remove init script via: $ pm2 unstartup systemd ubuntu@ubuntu:/opt/frp_0.37.0_linux_arm64$

Run pm2 startup, then run the command that pops up sudo env PATH=$PATH:/opt/node-v14.18.0-linux-arm64/bin /opt/node-v14.18.0-linux-arm64/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu

To verify that the automatic startup is set correctly, I pulled the power from the Raspberry Pi, and indeed the HTTP service was unavailable

Powering the Raspberry Pi on, frp.v2fy.com can be accessed again

Adding More Capacity to Raspberry Pi (with a 32GB USB Drive)

The SD card I am using is only 16GB, which is too small, so I plugged a 32GB USB drive into the Raspberry Pi for expanded capacity. After inserting the USB drive into the Raspberry Pi, enter the following command to see if the USB drive is recognized:

sudo fdisk -l

The USB drive is located at /dev/sda1.

  • Mount /dev/sda1 to /mnt/sda1:
sudo mkdir /mnt/sda1
sudo mount /dev/sda1 /mnt/sda1

Enter /mnt/sda1, and you can read and write files to the USB drive.

We can also mount the USB drive to the web service folder:

sudo mkdir /opt/frp.v2fy.com/sda1
sudo mount /dev/sda1 /opt/frp.v2fy.com/sda1

Once again, visit https://frp.v2fy.com, and you can access all the files on the USB drive via https://frp.v2fy.com/sda1.

What Can You Do with a Server?

Conclusion

Through this operation, I provided a domain name frp.v2fy.com for the home-use Raspberry Pi by using internal network penetration, which can be accessed via the public network; by configuring ssh, it is possible to remotely log in using ssh to the frp.v2fy.com port 6000 from anywhere on the internet.

Recently zhaoolee has been researching K8S clusters and needed a few high-performance hosts, but memory and hard drives are too expensive, so he put the Raspberry Pi 4B to use. With internal network penetration, it can serve like a real server on the internet.

Setting up your own server at home is every boy’s dream. As long as there are no power outages, no internet disconnections, and the Raspberry Pi board doesn’t explode, we can have an extremely cost-effective server at a very low cost.

--

--