Tags: /
networking
/
If you spot mistakes in this blog post, please let me know.
I currently have access to a desktop computer and a laptop computer. Sometimes I need the files in one of these computers available in the other computer.
Then I found that one of my friends is using ssh to allow remote access to their desktop.
We are in our college's network. So that meant less effort to set up ssh for remote access.
Note: Whatever is mentioned here works only if both server and clients are on the same network.
For ssh, the server and client software components components need to be installed.
In debian-based Linux distros, the package names are:
Wasn't sure if the server needed the client software as well, but I installed both components in both client and server.
(We can ssh into the server from the server itself, if the server has both server and client ssh software components installed.)
Now that we have the software part ready and the ssh daemon is running in the server, we can connect to the server. For that we need to know its IP address (this seems to work merely because both the server and the clients are within the same network).
For this, we could use ip
$ # Show protocol (IP or IPv6) addresses on a device
$ ip address
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp8s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 1c:69:7a:e3:5e:f0 brd ff:ff:ff:ff:ff:ff
3: wlp7s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 14:18:c3:02:be:08 brd ff:ff:ff:ff:ff:ff
inet 18.52.3.125/21 brd 18.52.3.255 scope global dynamic noprefixroute wlp7s0
valid_lft 78811sec preferred_lft 78811sec
inet6 ee80::7bff:ad1:b469:815c/64 scope link noprefixroute
valid_lft forever preferred_lft forever
(ip a
seems to do the same thing as ip address
.)
In my case, I got
en
prefix means ethernet)wl
prefix means wireless local area network)See this for the naming scheme.
lo
denotes loopback interface (localhost. Anything the computer sends over this connection is being sent to itself). Its IP address is usually 127.0.0.1
(ipv4) or ::1
(ipv6). localhost
is its domain name.
I need to use the wireless connection. So the address that I need is the one corresponding to wlp7s0
, which is 18.52.3.125
.
We could also have used ifconfig
command:
$ ifconfig
enp8s0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 1c:69:7a:e3:5e:f0 txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 1068 bytes 64584 (63.0 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1068 bytes 64584 (63.0 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
wlp7s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 18.52.3.125 netmask 255.255.248.0 broadcast 18.52.3.255
inet6 ee80::7bff:ad1:b469:815c prefixlen 64 scopeid 0x20<link>
ether 14:18:c3:02:be:08 txqueuelen 1000 (Ethernet)
RX packets 11771740 bytes 2112625907 (1.9 GiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1043530 bytes 165447258 (157.7 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
(Looks like my wifi connection had been quite active…)
Although people well-versed in Linux seem to prefer ip
over ifconfig
. See this.
In the output of ifconfig and ip, we can see ipv4 addresses followed by a slash and a number.
These are IP addresses written in the CIDR (Classless Inter Domain Routing) notation.
(For a small history behind the 'class' in the name of CIDR, see the addendum at the end of this blog post.)
The number after the slash indicates how many bits (known as prefix) of the ip address are used to address the network. The remaining bits are used to address the node inside that network.
For example, consider the ipv4 address 192.4.16.0/20
.
11000000.00000100.0001 0000.00000000
| | | |
+--------------------+ +-----------+
network part host part
(20 bits) (12 bits)
So, the network denoted by 192.4.16.0/20 can have at most 2¹² hosts (as exactly 12 bits are available for addressing the hosts).
ie, range of addresses available for hosts of this network is: 192.4.16.0
to 192.4.255.255
A few more examples: ¹³.
Network | Host addresses | Subnet mask |
---|---|---|
10.39.25.151/24 |
10.39.25.0 to 10.39.25.255 |
255.255.255.0 |
192.0.2.0/29 |
192.0.2.0 to 192.0.2.7 |
255.255.255.248 |
CIDR notation can be used for ipv6 addresses as well. As in 2001:db8::/48
denoting addresses from 2001:db8:0:0:0:0:0:0
to 2001:db8:ffff:ffff:ffff:ffff:ffff:ffff
. ¹⁴
2001:0db8:0000:0000:0000:0000:0000:0000
| | | |
+---------------------------+ +-------+
network part host part
(48 bits) (16 bits)
(IPv6 addresses are 16 bytes, while IPv4 addresses are 4 bytes.)
I prefer to use ssh keys instead of passwords to access remote machines (you should too!). For that I first need to make a key pair.
There are lots of guides to make ssh keys these days (like this, this and this), but I included the command and its output here nevertheless.
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/myusername/.ssh/id_rsa): /home/myusername/.ssh/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/myusername/.ssh/id_rsa
Your public key has been saved in /home/myusername/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:5hrSi4l1XdkYZZkGBGn87nuG3SIxVjBiLxETRWh05kg myusername@host
The key's randomart image is:
+---[RSA 3072]----+
| .+*B*= |
| ..*+* |
| o.=.= . |
| . Fo* +=. |
| . =So+=.. |
| = .o..+ o . |
| ..o .+ o . |
| o .+ . E |
| . o.o. +oo |
+----[SHA256]-----+
Right, now have got an ssh key pair with which we use to get authenticated to our remote server. But before using the key, we need to let the server know that this is the key that we are gonna use by 'installing' the key in the server.
We can use ssh-copy-id
command for this.
I had got my ssh public key (id_rsa.pub
) in my server and sort of installed the to the server itself by naming localhost
as the 'target'.
$ ssh-copy-id -i ~/.ssh/id_rsa.pub localhost
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/myusername/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
myusername@localhost's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'localhost'"
and check to make sure that only the key(s) you wanted were added.
As advised by the ssh-copy-id
's output, let's try if we can log in using this newly added key.
We use the private key (in my case, id_rsa
and not id_rsa.pub
) while logging into the remote server.
$ ssh -i ~/.ssh/id_rsa localhost
Enter passphrase for key '/home/myusername/.ssh/id_rsa':
Linux myhostname 5.10.0-10-amd64 #1 SMP Debian 5.10.84-1 (2021-12-08) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
$
Cool. It worked!
Now I make some changes to the way ssh is configured in the server.
(I referred to this nice blog post by Zach Duey for this part.)
We make changes to the configuration by editing the /etc/ssh/sshd_config
file.
Note: Each time after editing this file, we got to 'reload' the ssh daemon.
For setups using systemd
as init system, we can restart sshd
with
sudo systemctl reload sshd
and the changes that we made should take effect.
Default port used by ssh is port 22. I didn't change that.
I am using ssh keys to log in as it is relative more secure, and don't want to allow logging in passwords. So I disable the authentication based on passwords with:
PasswordAuthentication no
I may be having many user accounts in my server computer, but I don't want ssh login for most of them. So I restrict ssh login to some specific users.
# can log-in to server via ssh only as 'myusername'
AllowUsers myusername
I don't want to allow login as root via ssh. So I did:
PermitRootLogin no
(This may be redundant as I already used AllowUser
.)
I guess it's a good idea to put a limit on the maximum number of concurrent unauthenticated connections to server:
# Set maximum number of concurrent connections to 3
MaxStartups 3
ssh has the option of sort of 'forwarding' the GUI of an application running on server to the client.
I didn't want to use GUI via ssh. So I'm disabled that option with:
X11Forwarding no
It's possible to forward traffic incident on server ports to ports in client.
I disabled this forwarding traffic.
AllowTcpForwarding no
Now that we got an ssh service to which we can log into, we could use ways to work with it.
For example, to see if the ssh service is active, we could do (if the init system is systemd):
$ systemctl status ssh
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2022-04-04 12:02:26 UTC; 5h 19min ago
Docs: man:sshd(8)
man:sshd_config(5)
Main PID: 109728 (sshd)
Tasks: 1 (limit: 37689)
Memory: 1.1M
CPU: 422ms
CGroup: /system.slice/ssh.service
└─109728 sshd: /usr/sbin/sshd -D [listener] 0 of 2-2 startups
I found ssh connect/disconnect history at /var/log/auth.log
.
It had stuff like:
Apr 4 12:44:02 myhostname sshd[109728]: Server listening on 0.0.0.0 port 22.
Apr 4 12:44:02 myhostname sshd[109728]: Server listening on :: port 22.
Apr 4 12:48:50 myhostname sshd[111826]: Accepted publickey for myusername from 18.52.3.125 port 51760 ssh2: RSA SHA256:5Ben24j23SnbsjcjrMqfsa4T3FVkIwyvnqjNFNsnnxt
Apr 4 12:48:50 myhostname sshd[111826]: pam_unix(sshd:session): session opened for user myusername(uid=1000) by (uid=0)
Apr 4 12:48:55 myhostname sshd[111848]: Received disconnect from 18.52.3.125 port 51760:11: disconnected by user
Apr 4 12:48:55 myhostname sshd[111848]: Disconnected from user myusername 18.52.3.125 port 51760
Apr 4 12:48:55 myhostname sshd[111826]: pam_unix(sshd:session): session closed for user myusername
We can also see failed login attempts here:
Apr 4 12:37:43 myhostname sshd[111575]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=::1 user=myusername
Apr 4 12:37:46 myhostname sshd[111575]: Failed password for myusername from ::1 port 39414 ssh2
Tools like grep
and awk
could come handy in finding the things you need here.
When the client disconnects from the server over an ssh connection, every process that the client had started at the host will be terminated.
That's a bit inconvenient if we have long-running jobs as we won't be able to disconnect till the end of the job execution, which might take days.
To get around this problem, we could use screen or tmux.
After logging into the server start screen/tmux and then start the process within it.
Now you can disconnect from the server and the process will keep executing as if the server didn't realize that we had left.
We can later come back and check on the job execution without breaking a sweat.
Found a few other ways as well here. I haven't (yet) tried any of these, though.
I had only known of tmux/screen. Had forgotten about nohup
, didn't even know about disown
.
There are fancier tools like byobu (which can work on top of screen or tmux) as well.
See this for a related discussion.
Could use one of rsync
and scp
. I got the idea that rsync
is usually better.
With rsync
, we can copy from client to server with:
rsync -rve "ssh -i /path/to/private-key" /path/to/client-file remoteuser@remoteip:/path/to/remote-file
Similarly, copying from server to client can be accomplished with:
rsync -rve "ssh -i /path/to/private-key" remoteuser@remoteip:/path/to/remote-file /path/to/client-file
rsync
needs to be installed on both client and server. This is true in the case of scp
as well.
This is the error that I got when I tried using rsync
when only client had it:
bash: line 1: rsync: command not found
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: remote command not found (code 127) at io.c(235) [Receiver=3.1.2]
Not exactly informative, but easy enough to figure out with an internet search, I guess.
Adding an ssh key to a server was as easy as using ssh-copy-id
, so I expected there would be an similar way to remove a previously added key from the server as well.
Well apparently, there's no such way.
One way that I found here is:
/etc/ssh/sshd_config
AuthorizedKeysFile:
and note the file paths after it.In my case, the key was in a file at ~/.ssh/authorized_keys
.
This is essentially reversing what ssh-copy-id
did.
From here:
ssh-copy-id
uses the SSH protocol to connect to the target host and upload the SSH user key. The command edits theauthorized_keys
file on the server. The.ssh
directory is created if it doesn't exist. It also creates theauthorized_keys
file if it doesn't exist.
sshd_config
man page(The ip addresses mentioned in output of some commands mentioned in this blog past are not real.)
The 'class' in the name 'Classless Inter-Domain Routing' has got to do with an addressing scheme that was in use before it.
Before CIDR, there were classful networks, where IP addresses were grouped into different classes.
In classful networks, IP addresses (32 bits) were grouped into 5 classes:
1b 7b 24b
+---+-----------+------+
| 0 | Network | Host | Class A
+---+-----------+------+
2b 14b 16b
+----+-----------+------+
| 10 | Network | Host | Class B
+----+-----------+------+
3b 21b 8b
+-----+-----------+------+
| 110 | Network | Host | Class C
+-----+-----------+------+
Class | Networks# | Hosts# |
---|---|---|
A | 2⁷ | 2²⁴ |
B | 2¹⁴ | 2¹⁶ |
C | 2²¹ | 2⁸ |
Maximum number of hosts is actually two less than what's shown in the above table as the address with all bits in the host part set is the broadcast address and that with all host bits set to zero is the address of the network itself. ²² (I guess that's why localhost is 127.0.0.1
and not 127.0.0.0
).
But soon it became evident that classful addresses don't scale that well.
From Computer Networks: A systems approach:
The original idea was that the Internet would consist of a small number of wide area networks (these would be class A networks), a modest number of site-(campus-)sized networks (these would be class B networks), anda large number of LANs (these would be class C networks). However, it turned out not to be flexible enough
One problem was that classful addresses could lead to unused addresses.
For example, if a network needed 2⁸+1 host addresses, the addresses would be of class B. That means that 2¹⁶-(2⁸+1) = 65279 host addresses would remain unused. This would be even more if the number of host addresses required is at boundary of the number of hosts supported by class A and class B. That could mean huge wastage of address space. (Also see Wikipedia: IPv4 address exhaustion. Hadn't realized that we had already run out of IPv4 addresses…)
In contrast to this scenario, with CIDR, the number of bits to be used to address networks is totally flexible. CIDR IP addresses sort of comes with a subnet mask (represented as the slash prefix like /24
) which can be used to distinguish between the network and host part.
For example, if an address of a network is 192.45.23.5/24
, the subnet mask is 255.255.255.0
as the /24
means that the first 24 bits are used to identify the sub-network and the remaining bits are for addressing the hosts.
If an ip address of a host of this network is 192.45.23.64
,
network part of address = address AND subnet-mask
11000000.00101101.00010111.01000000 = 192.45.23.64
& 11111111.11111111.11111111.00000000 = 255.255.255.0
------------------------------------------------------------
11000000.00101101.00010111.00000000 = 192.45.23.0
host part of address = address AND (one's complement of subnet-mask)
11000000.00101101.00010111.01000000 = 192.45.23.64
& 00000000.00000000.00000000.11111111 = one's complement(255.255.255.0)
------------------------------------------------------------
00000000.00000000.00000000.01000000 = 0.0.0.64
In the case of classful networks, the mask was implied the network class itself. CIDR just makes it explicit.
CIDR equivalent for the classes of addresses of classful networks would be:
Class | CIDR |
---|---|
A | /8 |
B | /16 |
C | /24 |
—
Update (13-Aug-2022): Explicitly mention that init system should be systemd to use the systemctl
command.