How to set up a Linux server
Table of Contents
- Table of Contents
- Renting a (virtual) server
- Logging in the server
- Downloading and running a node (web) app
- Periodic commands
- Making an http/https web application
- Linux Game Server Manager
- Linux commands
- Making backups
Renting a (virtual) server
- Although you could host a website from your home computer (after dealing with opening the router ports and possibly dynamic IPs…), it’s generally more convienent, and cheaper, to outsource a machine that is always on.
- Many options, as in sellers and operative systems, exist today, and regardless of the seller, my favorite server operative system is Ubuntu, the most popular linux distribution (distro).
- The reasons I prefer Ubuntu/Linux:
- Since it’s the most popular Linux distro, there’s generally more support and more software available
- It’s a Linux OS, which means it is “Unix” (as a hipster computer aficionado I like to use UNIX terminals)
- Linux servers are provided by most sellers and are cheaper than Windows (fewer switching costs in the future)
- I’m currently using the cheapest cloud server offered by Hetzner. Apparently it’s a virtual CPU what I have access to, but from a practical point of view, it works and feels like if it was a regular Linux machine always connected to the internet. Therefore this detail is beyond the scope of this tutorial.
Logging in the server
- After you rented the server for the first time you should receive login credentials from the seller, such as a
root
username and a temporary password. - Yo should be able to access to the server via some built-in console link for the web browser in the seller website. And you’ll probably be prompted to change your password after you sign in for the first time
- Attention: Do not use a weak password (yet, at least). There are many bots out there bruteforcing random IPs on the internet and as a matter of fact, I get like 10 loging attempts from bots per second since the beginning
- Now that we are in, we should create a non-admin user from which we will log in (via the terminal, the IDE, etc) as we will shortly disable
root
(which is admin) log in via “SSH” (secure shell, once we get in the server via the non-admin user we can log in as admin, this is safer because bots target theroot
user, but they are less likely to target your non-admin username)
Create a user
- (In the built-in terminal in the web browser for the server) Create the non-admin user “alex” (for the sake of this tutorial) with the following command:
useradd -m alex
-m creates a home directory for alex in /home/alex
which alex owns and has full rights
- To be able to log in for the first time as alex we need to add a password:
passwd alex
- Which will prompt you to type and retype the password. At least for now, try to make it secure, as bots might still be able to bruteforce the username and a weak password.
SSH Login
- Sweet, we have a non-admin users that we can use to log in via ssh. Let’s just give it a try by “sshing” to the server IP (which for the sake of this tutorial is 157.90.233.167) from our local machine:
- For Linux terminals:
ssh alex@157.90.233.167
-
If you don’t have a Unix terminal, aka Windows, then install the
wsl
program for the command line. It’s Window Subsystem Linux and you can find it on Windows appstore. Then go to the command line (cmd
) and enterwsl
. Then follow the tutorial for Linux -
You should be prompted to enter your password
alex@157.90.233.167's password:
, but if instead you get the messagessh: connect to host 157.90.233.167 port 22: Connection refused
you may run this on the server:- Make sure you have
openssh-server
installed - As root, run
sudo service ssh start
- Then from the local machine try to ssh into the IP with the user we created
- Make sure you have
Disable root login
- People can still try to bruteforce
root
passwords. Since we can at least log in via the non-admin useralex
, let’s first disable ssh root logins - In the server, as root, run
vim /etc/ssh/sshd_config
and editPermitRootLogin yes
line toPermitRootLogin no
.- Since we last logged in as
alex
, just runsu
to log in as root (and type the prompted password). - If you need help with vim type
vimtutor
(for this you only need to press A (to insert) use the arrows to navigate the pointer, make the changes, then press Escape key, then type:wq
and hit Enter key. - To activate this change, as root, run
service ssh restart
- Since we last logged in as
Login with public key
- A public-private key log in is a type of encryption handshake where the party that needs to send something (i.e. login API) looks up the public key of the requesting party, such that that public key is used to encrypt a message that only the other party can decrypt with his private key. For more details check Computer Networks Security Notes
- We’ll generate a public key for the local machine with the following command:
ssh-keygen
- Youll be prompted for the location of the file, accept the default or give your own location. You may skip the passphrase but it’s more secure not to (I skip it).
- You should recive a text with the location for
id_rsa
andid_rsa.pub
(if the default algorithm was RSA), such as/home/localmachineuser/.ssh
cd
into that location, then copy your public key to the server with:
ssh-copy-id -i id_rsa.pub alex@157.90.233.167
- Your private key must be only known to you, so that if senders use your public key and an attacker interferes the message, it wont be able to decrypt it since he needs your secret/private key.
- After hitting the command you’ll be prompted to enter the password, then it’ll notify you of the number of keys added (1) and it’ll suggest you to
ssh 'alex@157.90.233.167'
- If you are on
wsl
it’ll complain that the private key is unprotected. If it indeed is a problem and doesn’t let youssh alex@157.90.233.167
, then fix it with the code below based on this
# You are might be already in /mnt/c, so get outta there before unmounting it
cd /
sudo umount /mnt/c
sudo mount -t drvfs C: /mnt/c -o metadata
# now you can ssh alex@157.90.233.167
Disable password login
- Although we can log in directly with the public key (you should have noticed that it doesnt prompt you for the password anymore), people (hackers or specially bots) from other devices can still request a password ssh login. We don’t use it and it poses a security threat, we block the possibility to login via ssh with password by editing
PasswordAuthentication yes
toPasswordAuthentication no
from the/etc/ssh/sshd_config
file:- In the server, as root
vim /etc/ssh/sshd_config
and make the change - Then
service ssh restart
to implement the new configuraion.
- In the server, as root
- Now if someone wants to log in to
ssh alex@157.90.233.167
and doesn’t have the private key of alex, or hasn’t stored his public key on the server for alex user, then it will receivealex@157.90.233.167: Permission denied (publickey).
Monitoring successful login attempts
- If you want to double check that the IPs or access style (public key instead of password, which users, etc) matches your expectations then as
root
in the server run this:
cat /var/log/auth.log | egrep Accepted
- Keep in mind that older logs are stored in other files such as auth.log.1, auth.log.2, etc.
Banning bruteforce attempts with fail2ban
- Just innstall it as root
apt install fail2ban
- The default settings should do the job for
ssh
login bans, but feel free to explore the rest of the program. - If you want to be sure
vim /etc/fail2ban/jail.local
and in the text editor, type in the following text after [sshd]
[sshd]
enabled = true
- You can double check it’s status with
fail2ban-client status sshd
Downloading and running a node (web) app
- We have a server to share things with others right? Well, I do. Let’s download a simple web app as example
- ssh into the server, then on your desire path run:
git clone https://github.com/skirienkopanea/3d
cd 3d
npm install
- For this repo, you can run it with
npm start
, which will host it on port 3000, or you cannode app 4269
, to specify a custom port (4269 in this case) - You can find the app running on the browser at address
http://157.90.233.167:4269
- If you want to push git changes you’ll need to make sure to set up the git configuration for that user:
git config --global user.email "alex@kpan.nl"
git config --global user.name "alex"
Detaching sessions
- If you close the terminal, you’ll see that the browser no longer responds to the previous address!
- What a bummer! We rent a server to run apps all day but that requires to have the terminal window of our local machine also operative… not really!
- To avoid that scenario, just run
tmux new -s myappname
, and then you can run therenode app 4269
, then hit Ctrl+B then D, to detach from the session and leave the app running also after closing your local terminal. - You can use
tmux ls
to show all sessions - To connect to a session
tmux attach -t myappname
- To end a session hit Ctrl+C to end the running program, then run
exit
- You can open a session, run the command there, and detach (without stopping it) all at once with:
tmux new -d -s 3d 'cd /home/alex/3d; node app 4269'
Periodic commands
- Since the server may crash, or be restarted by you, the sessions will disappear and the app will no longer be hosted
- You can schedule periodic commands (such as the one that hosts the app in a detached session) in the
crontab
file. - For a non admin user you can view it with
crontab -l
, edit it withcrontab -e
and for the node application we can create a periodic task of running the command of the previous section every 5 minutes:
*/5 * * * * tmux new -d -s 3d 'cd /home/sergio/3d; node app 3000'
- Where the 5 placeholder values stand for minute, hour, day of the month, month, day of the week.
- For more info check
man crontab
- For the root it may only work if you edit the crontab directly: as root run
vim /etc/crontab
PM2
- Since the application may crash and not the session, it may be possible to have an open session that prevents the crontab to start a session and run the command, because the session name is already taken.
- Therefore, for this purpose we can instead use
pm2 start app -- yourargumentsthatyoudusewithnodecommands
inside the sessions instead ofnode app 4269
(make sure to specify a non-used port inpackage.json
start line). This will make sure to run the node app on that session after a crash. - Install
pm2
as root withnpm install pm2 -g
- edit
package.json
to have a port that doesnt conflict with other running processes and that you hace access to, such as 4269. - No longer need to open a session, pm2 takes care of runing things in the background, run
pm2 start app.js -- 4269
- “pm2 start”, besides it’s extra features, it’s the equivalent of just “node”, so
pm2 start app.js
is not “pm2ing”npm start
(for which you can set a custom “start”: … line in package.json), it’s the equivalent ofnode app
, which by default might assumeprocess.env.PORT
3000. - If you run PORT=3003 node index.js, Node will make
process.env.PORT
equal to 3003 - That’s why, if you haven’t hardcoded the port, we use for this example
pm2 start app.js -- 4269
as the equivalent ofnode app 4269
- “pm2 start”, besides it’s extra features, it’s the equivalent of just “node”, so
- Use
--name
such that you can distinguish the apps:start app.js --name 3d -- 4269
- Use
pm2 logs
to see the logs of all the apps - Run this command as
root
to ensure that any applications it saved restart when the server reboots. Basically, your node application will start as a service.- You can get it executing
pm2 startup
as well
- You can get it executing
env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u alex --hp /home/alex
- To save the applications that will be restarted after a reboot you have tu run
pm2 save
- To remove one node process
pm2 del appname
and all node processespm2 kill
- With these pm2 commands we no longer need crontabs for npm applications. And although non-saved processes are still gonna be listed after a reboot, these wont be run unless they were saved. So we can have just 1 crontab task, which is to
pm2 save
before a reboot. - Edit the /etc/crontab with root priviliges and have somethig like:
0 3 * * * root shutdown -r now
55 2 * * * alex pm2 save
55 2 * * * sergio pm2 save
Making an http/https web application
Give node permission to use lower ports
- Ports lower than 1024, such as 80 (http) and 443 (https) are reserved for root, but we can now allow node apps without root to also access those (while not having root privileges, as we want) with:
apt-get install libcap2-bin
setcap cap_net_bind_service=+ep /usr/bin/node
Other sources suggest the path /user/local/bin/node. Just doublecheck which one you have.
Getting a domain and SSL certificates for https (http secure)
- IPs are long and hard to remember. Get a domain and config
A
with IPv4 of your server andAAAA
with IPv6 - Getting certificates is free with certbot, as root:
- Install
snapd
- Install
certbot
- Disable the apps using port 80 temporarily
- Run
certbot certonly --standalone
which will use that port to get the certificate - You can expand to more domains later with
certbot certonly --expand -d kpan.nl,alex.kpan.nl,sergio.kpan.nl
- Easiest prompted option is to first disable other apps using port 80 and choose the temprary webserver option.
- When you “expand” you still need to put the older domains you had (otherwise they wont longer be SSLed)
- The directory where they’re saved will probably be the first subdomain in alphabetical order
- Check where the certs are saved, and keep a copy there and another one in the web directory
- For these things it’s better to check the updated info on their website https://certbot.eff.org/
- Install
- I’ve set up a crontab for root to copy these certifaces (whose permissions need to be changed with
chmod
to allow the web non-admin user to read the copies)
45 2 14 * * root chmod 764 /locationgiven/privkey.pem; chmod 764 /locationgiven/fullchain.pem; cp /locationgiven/privkey.pem /home/alex/3d; cp /locationgiven/fullchain.pem /home/alex/3d#
Remote coding on Visual Studio Code
- To make and manage the web app we will use an IDE (ineractive development environment), which is better suited than
vim
for this. - Install the “Remote - SSH” extension
- If you encounter an error when connecting, you may need to enable socket forwarding on your SSH Host’s sshd config. To do so
vim /etc/ssh/sshd_config
and make sure to haveAllowStreamLocalForwarding yes
- You can follow the steps in these link1 link2
- If you’ve been following the whole tutorial from
wsl
, you will notice that “sshing” won’t work because the ssh that VSC is using is not fromwsl
. - Copy the keys you made from
wsl
to windows preferred location for ssh keysC:\Users\username\.ssh
- Double check in windows command line that you can run
ssh alex@157.90.233.167
, if that works, proceed to Connect to a hostalex@157.90.233.167
via VSC.
Transfering files
- We can code the thing comfortably via VSC, but we can also copy something from our local machine to the server directly.
- You can drag files from your windows/linux folder into the VSC explorer, or in unix, you can also run the
scp
command:scp sourcepath host:destinationpath
scp sourcepath1 sourcepath2 sourcepath3 host:destinationpath
https node app
- After clonging
3d
app, inapp.js
, had only support for http (middelware not shown):
//Import all packages (public) and modules (local)
var express = require("express");
var http = require("http");
//Port config, create Express application an create server
var port = process.argv[2];
var app = express();
var server = http.createServer(app);
server.listen(port);$
- We can have support for both http and https requests, in a way that http are redirected to https:
//Import all packages (public) and modules (local)
var express = require("express");
var http = require("http");
var https = require("https")
var fs = require("fs");
//Port config, create Express application an create server
var httpPort = process.argv[2];
var httpsPort = process.argv[3];
var httpApp = express();
var httpsApp = express();
var httpServer = http.createServer(httpApp);
var httpsServer = https.createServer({
key: fs.readFileSync("../../sergio/web/privkey.pem"),
cert: fs.readFileSync("../../sergio/web/fullchain.pem")
},httpsApp);
httpServer.listen(httpPort);
httpsServer.listen(httpsPort);
//redirect http requests to https
httpApp.get("*",function(req, res, next) {
res.redirect("https://" + req.hostname + req.url);
});
- Such that the app is run with
node app 3080 3443
orpm2 start app.js --name 3d -- 3080 3443
- However, this is a bit too much for a simple app, I’d just host both http and https ports for the main homepage of the website and have all the small apps be running only under https
Using a database for a Guestbook
- Let’s just complete the server with dynamic website
Install MySQL
- Important note, the mysql commands are case insensitive
- as root:
apt install mysql-server
- This
root
script changes some of the less secure default options for things like remote root logins and sample user:mysql_secure_installation
- as
root
enter the mysql prompt just runningmysql
Creating a non-admin user
- inside the mysql prompt you can create a new user with a CREATE USER statement:
CREATE USER 'username'@'host' IDENTIFIED WITH auth_socket;
- After CREATE USER, you specify a username. This is immediately followed by an @ sign and then the hostname from which this user will connect. If you only plan to access this user locally from your Ubuntu server, you can specify localhost. Wrapping both the username and host in single quotes isn’t always necessary, but doing so can help to prevent errors.
- you can use alternatives to auth_socket, but this plugin is cool as requires that the name of the operating system user that invokes the MySQL client matches the name of the MySQL user specified in the command
- If you dont define the authentication plugin mysql default is
caching_sha2_password
. i.e.CREATE USER 'sammy'@'localhost' IDENTIFIED BY 'password';
(which can be accesed by anyone on the server knowing the password) - There is a known issue with some versions of PHP that causes problems with caching_sha2_password. If you plan to use this database with a PHP application — phpMyAdmin, for example — you may want to create a user that will authenticate with the older, though still secure, mysql_native_password plugin instead
- If you aren’t sure, you can always create a user that authenticates with caching_sha2_plugin and then ALTER it later on with this command:
ALTER USER 'sammy'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
- After creating your new user, you can grant them the appropriate privileges. The general syntax for granting user privileges is as follows:
GRANT PRIVILEGE ON database.table TO 'username'@'host';
- The PRIVILEGE value in this example syntax defines what actions the user is allowed to perform on the specified database and table. You can grant multiple privileges to the same user in one command by separating each with a comma. You can also grant a user privileges globally by entering asterisks (*) in place of the database and table names. In SQL, asterisks are special characters used to represent “all” databases or tables.
- For example>]:
GRANT CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT, REFERENCES, RELOAD on *.* TO 'sammy'@'localhost' WITH GRANT OPTION;
- Note that this statement also includes WITH GRANT OPTION. This will allow your MySQL user to grant any that it has to other users on the system.
- You can also make the database before creating a user with global grants (
CREATE DATABASE dbname
)
- You can exit the mysql prompt (called client) with the command
exit
and try to log in from the non-root account: ` mysql -u sammy -p`. The -p flag will cause the MySQL client to prompt you for your MySQL user’s password in order to authenticate. - However, I’ve noticed that for the web app node doesn’t support auth_socket, so just stick to
mysql_native_password
Testing the installation
- Regardless of how you installed it, MySQL should have started running automatically. To test this, check its status.
systemctl status mysql.service
- If MySQL isn’t running, you can start it with sudo
systemctl start mysql
Creating a table
- Inside the mysql we will first make the database (schema) where the table will be:
create database sergioweb;
- You can double check that you created it with
SHOW DATABASES;
. - Then run
USE sergioweb;
to switch to that database. - Then we will create a table for the guestbook with the following attributes
- id (auto incr)
- alias
- comment
- timestamp
CREATE TABLE guestbook (
id int NOT NULL AUTO_INCREMENT,
alias varchar(100) NOT NULL,
comment varchar(255) NOT NULL,
dbtime timestamp NOT NULL,
PRIMARY KEY (id)
);
Testing with a sample record
- Let’s test it with:
INSERT INTO sergioweb.guestbook (alias, comment, dbtime)
VALUES ('sergio', 'hola', NOW());
- Let’s see it with:
SELECT * FROM sergioweb.guestbook;
Node js server side
- Add the following lines to the
app.js
file that you will use to process the queries on the server side, and then send the data to an ejs file: - But don’t forget to first install mysql on the node app, go to its directory and run
npm install --save mysql2
- Install
npm install --save express-validator
to validate web form inputs andnpm install --save body-parser
to get them in the first place.
//Import all libraries (third party such as ws) and modules (local)
var express = require('express');
var mysql = require('mysql2');
var connection = mysql.createConnection({
host : 'localhost',
user : 'sergio',
password: '',
database : 'sergioweb'
});
const bodyParser = require("body-parser");
const {body,validationResult } = require('express-validator');
//...
// To get the comments
app.get('/guestbook.html',function(req, res, next) {
connection.query('SELECT * FROM guestbook ORDER BY dbtime DESC;', function(err, results, fields) {
if (err) {
console.log(err);
res.render("error");
};
res.locals.db = results;
res.render('guestbook.ejs');
});
});
// To post a comment
app.use(bodyParser.urlencoded({
extended:true
}));
app.post('/guestbook',[
body("alias", "Alias must have a value.").not().isEmpty(),
body("comment", "Comment must have a value.").not().isEmpty()
],function(req, res, next) {
const errors = validationResult(req);
if(!errors.isEmpty()) {
console.log(errors);
return res.status(500).json({
errors
});
}
console.log(req.body.alias);
console.log(req.body.comment);
connection.query("INSERT INTO sergioweb.guestbook (alias, comment, dbtime) VALUES ("
+ mysql.escape(req.body.alias) + ", " + mysql.escape(req.body.comment) + ", NOW());", function(err, results, fields) {
if (err) {
console.log(err);
res.locals.message = err.message;
res.locals.error = err;
res.render("error");
};
res.redirect("/guestbook");
});
});
HTML/EJS implementation
- To just render the guestbook table
<table>
<% db.forEach(function(record) { %>
<tr>
<table>
<tr>
<td>
<%=record.alias%> on <%= record.dbtime.toDateString()%>:
</td>
</tr>
<tr>
<td><%= record.comment%></td>
</tr>
</table>
</tr>
<% }); %>
</table>
- The form to post a comment:
<form action="/guestbook" method="post">
<table>
<tr>
<th>Alias</th>
<th>Comment</th>
</tr>
<tr>
<td>
<input type="text" id="alias" name="alias" maxlength="25" value="" size="10%">
</td>
<td>
<input type="text" id="comment" name="comment" maxlength="250" value="" size="90%">
</td>
</tr>
</table>
<input type="submit" value="Submit">
</form>
Reducing memory
- You’ll notice that mysql uses a lot of memory. To minimize it (at the expense of performance), as root kill all processes of user
mysql
:pkill -U mysql
- and then change the configuration:
root@server:/home/sergio# vim /etc/mysql/my.cnf
# The MySQL database server configuration file.
[mysqld]
performance_schema = off
key_buffer_size = 1M
innodb_buffer_pool_size = 10M
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
Linux Game Server Manager
- Dont host the servers as root
- Go to https://linuxgsm.com/servers/ and look for the game server you’d like to host, such as Counter-Strike 1.6:
- Install dependencies
sudo dpkg --add-architecture i386; sudo apt update; sudo apt install curl wget file tar bzip2 gzip unzip bsdmainutils python util-linux ca-certificates binutils bc jq tmux netcat lib32gcc1 lib32stdc++6 libsdl2-2.0-0:i386 steamcmd
- As non-root, download linuxgsm.sh for counter strike:
wget -O linuxgsm.sh https://linuxgsm.sh && chmod +x linuxgsm.sh && bash linuxgsm.sh csserver
- Run the installer:
./csserver install
- A complete list of commands can be found by typing
./csserver
- Recommended cron tasks below:
- Install dependencies
*/5 * * * * /home/csserver/csserver monitor > /dev/null 2>&1
*/30 * * * * /home/csserver/csserver update > /dev/null 2>&1
0 0 * * 0 /home/csserver/csserver update-lgsm > /dev/null 2>&1
- For minecraft you can do the same, or you may download the server file yourself from minecraft (or a third party, especially for older versions) website and run the server with these minimal configuration for 2 players:
java -Xmx1024M -Xms512M -jar server.jar --port 27018 nogui
Linux commands
- One of my favorite server commands is
htop
to see the CPU, Memory and running processes - Then
su - username
to log in tmux ls
,tmux attach -t
,tmux new -s sessionname
(use Ctrl + B then D to detach)crontab -l
,crontab -e
pm2 start app.js --name appname -- args
pm2 save
pm2 del appname
pm2 kill
shutdown -r now
to restartclear
to clean the terminalping kpan.nl
ssh user@kpan.nl
scp src user@kpan.nl:dest
du -sh path
to check the disk usage of the path, ordf
- You can find more linux commands in this post
Making backups
- We’re gonna make a partial disk backup, for the things that we care
- I don’t care about old archived logs, temp files, nor programs I can install in the terminal.
- The following directories contain the contents of the users (which includes pm2 process lists) and the crontabs. I do not want to send sshd configuration nor keys. I’d rather redo those things manually if necessary.
/home
/var/spool/cron/crontabs
/etc/crontab
- Since the crontabs have root priviliges, let’s just keep a copy of them under
/home/sergio/crontabs/
- As root:
cp -r /var/spool/cron/crontabs /home/sergio/crontabs; cp -r /etc/crontab /home/sergio/crontabs/crontab; chmod -R 777 /home/sergio/crontabs
- As root:
- In the local machine where you are going to store the backup first do this:
From the server to the local machine
Create ssh server on the local machine
sudo apt install openssh-server
sudo systemctl start ssh.service ## <-- Linux start sshd ##
## Other commands:
sudo systemctl stop sshd.service ## <-- stop the server ##
sudo systemctl restart sshd.service ## <-- restart the server ##
sudo systemctl status sshd.service ## <-- Get the current status of the server ##
- Debug that you can
ssh localhost
ssh localipaddress
- You can get the local ip address running
ifconfig
- You can get the local ip address running
- On the server, generate a key with
ssh-keygen
. If one exists, don’t overwrite it, use that one. - Then open your internet provider port 22 for the local IP address of your machine
- On the local machine make sure that the sshd server is on
- Then try to
ssh publicIPaddress
Server side commannds
- Back on the server, run
ssh-copy-id -i id_rsa.pub sergio@86.83.17.164
- Then you might want to immedieately disable ssh password login on the local machine
sudo vim /etc/ssh/sshd_config
and follow the steps from this part. - Then on the server run the followign backup script:
scp -r /home sergio@86.83.17.164:/home/server-backup
- When you’re done you can
sudo systemctl stop sshd.service
on the local machine - It’d be nice to have a crontab on the server that back ups the files, however, the local machine is rarely online. Instead we should initialize the backups from the local machine.
From the local machine request back up to the server
- We don’t actually need to set up an ssh server on the local machine.
- From the local machine we can just run:
scp -r sergio@kpan.nl:/home /home/server-backup