Exploring Linux Basics

In this post, we’ll explore some essential Linux commands that are vital for effective management, monitoring, and configuration of files and system settings. These commands will help us to navigate and control the Linux environment with ease.

User Management


The objectives for this task were to create users and groups, set up user directories, manage permissions, and configure user-specific settings. We’re going to start with creating a specific user’s group called advanced_users with GID set to 1337.

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo groupadd -g 1337 advanced_students

In the next step, we’re going to add a new user student2. Upon creation, we’re going to set UID to 1001 and assign the 1337 ad the GID from the group we’ve previously created (the user will be added to advanced_students group based on this GID), as well as we are going to set a custom home directory for the user:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo useradd -u 1001 -g 1337 -d /home/advanced_users/student2 -m student2

-m flag makes sure that the specified directory will be created in case it doesn’t already exist.

Next, we’ll apply a password expiration policy for the newly added user, so that the password will need to be changed every 30 days:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo chage -M 30 student2  

To check if the policy was applied, we use sudo change -l student2 like this:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo chage -l student2   
Last password change                                    : Oct 30, 2024
Password expires                                        : Nov 29, 2024
Password inactive                                       : never
Account expires                                         : never
Minimum number of days between password change          : 0
Maximum number of days between password change          : 30
Number of days of warning before password expires       : 7

Next, we’ll set the default shell for the user to zsh:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo chsh -s /bin/zsh student2

To check if the action was successful, we can do the following:

┌──(ognard㉿ognard)-[~/Practice]
└─$ grep "^student2:" /etc/passwd 
student2:x:1001:1337::/home/advanced_users/student2:/bin/zsh

Finally, we will demonstrate the ability to lock and unlock a particular user:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo usermod -L student2  

To check if the user was successfully locked:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo passwd -S student2     
student2 L 2024-10-30 0 30 7 -1

Having L after the username, signifies that the user was locked. To unlock use:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo usermod -U student2             
usermod: unlocking the user's password would result in a passwordless account.
You should set a password with usermod -p to unlock this user's password.

If there isn’t password for the user, then it has to be set before unlocking it. Suggestion points that you can use sudo usermod <user> -p <password> to set the password, however you should be aware that this action may reveal the newly set password through terminal’s history.

Process Management


The objectives for this task are to start, monitor, and manage processes using advanced tools and techniques.

For this one, we are going to start with long running process; we will use ping command set to run in the background and keep look of its action:

┌──(ognard㉿ognard)-[~/Practice]
└─$ ping ognard.com > ping_log.txt 2>&1 &
[1] 23604

The log will be stored as ping_log.txt and we’ll catch any outputs in Standard Output (stdout) and Standard Error (stderr) with 2>&1.

Next we will find the ongoing process by using ps like this:

┌──(ognard㉿ognard)-[~/Practice]
└─$ ps aux | grep ping                   
ognard     26267  0.0  0.0   9512  3200 pts/0    SN   20:22   0:00 ping ognard.com

Additionally, we can use htop to find the running process. Once htop is opened, you can hit F3 to search for ping and it will show up the process information.

htop Preview

Once we have found the PID for the running process, we can also use renice to change the priority of the process. For example:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo renice +10 -p 34259             
34259 (process ID) old priority 5, new priority 10

For example, changing priority of the process is useful for adjustment of the resources consumption (CPU) in relation to other running processes.

Finally, to trace system calls that were made by the ping process we can use strace:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo strace -p 35193   
strace: Process 35193 attached
restart_syscall(<... resuming interrupted read ...>) = 0
sendto(3, "\10\0\211\374\211y\0\36\207\213\"g\0\0\0\0u\246\6\0\0\0\0\0\20\21\22\23\24\25\26\27"..., 64, 0, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("104.21.84.127")}, 16) = 64
recvmsg(3, {msg_name={sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("104.21.84.127")}, msg_namelen=128 => 16, msg_iov=[{iov_base="E\0\0TS\236\0\0:\1o\303h\25T\177\300\250@\v\0\0\221\374\211y\0\36\207\213\"g"..., iov_len=192}], msg_iovlen=1, msg_control=[{cmsg_len=32, cmsg_level=SOL_SOCKET, cmsg_type=SO_TIMESTAMP_OLD, cmsg_data={tv_sec=1730317191, tv_usec=449569}}], msg_controllen=32, msg_flags=0}, 0) = 84
...

Practical Usage of Commands


The objectives for this task is to use a variety of system commands to perform complex tasks. We’ll start with creating a directory structure and setting appropriate permissions. First, to create directories:

┌──(ognard㉿ognard)-[~/Practice]
└─$ mkdir dir_parent            
                           
┌──(ognard㉿ognard)-[~/Practice]
└─$ mkdir dir_parent/dir_child_1
                      
┌──(ognard㉿ognard)-[~/Practice]
└─$ mkdir dir_parent/dir_child_2
                  
┌──(ognard㉿ognard)-[~/Practice]
└─$ mkdir dir_parent/dir_child_3

The shorthand way to do this is mkdir -p parent_dir/{child_1,child_2,child_3}

To preview the structure, we can use tree command:

┌──(ognard㉿ognard)-[~/Practice]
└─$ tree dir_parent         
dir_parent
├── dir_child_1
├── dir_child_2
└── dir_child_3

To change permissions for dir_parent directory, we can use:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo chmod 644 dir_parent  

Cheatsheet: Read is 4, Write is 2 and Execute is 1.

This will set the permissions to read and write for the owner and read for the group and global . To verify the permissions, after ls -al we can see:

drw-r--r--  5 ognard ognard 4096 Oct 30 20:55 dir_parent
  • rw- for the owner
  • r– for the group
  • r– for global

Next coming up is copying files from one directory to another. First, for the purpose of this task, we will create an example file in dir_parent/dir_child_1:

┌──(ognard㉿ognard)-[~/Practice]
└─$ echo "Some text for the example file 1" > dir_parent/dir_child_1/example_file

After that, we will proceed with copying the file to another directory:

┌──(ognard㉿ognard)-[~/Practice]
└─$ cp dir_parent/dir_child_1/example_file dir_parent/dir_child_2

To verify the copying process, we can use tree:

┌──(ognard㉿ognard)-[~/Practice]
└─$ tree dir_parent       
dir_parent
├── dir_child_1
│   └── example_file
├── dir_child_2
│   └── example_file
└── dir_child_3

And we can see that the same file is in both folders - dir_child_1 and dir_child_2. Alternatively, we can navigate to the destination directory and check if the file is copied there as well.

Next is using rsync to synchronize directories and preserve permissions:

┌──(ognard㉿ognard)-[~/Practice]
└─$ rsync -av --progress ./src_dir ./dst_dir 
sending incremental file list
src_dir/

sent 75 bytes  received 20 bytes  190.00 bytes/sec
total size is 0  speedup is 0.00
  • -av will set Archive Mode and Verbose Output. Archive Mode will preserve permissions.
  • –progress will display progress during the transfer.

Another objective is to securely transfer files to a remote server using scp :

┌──(ognard㉿ognard)-[~/Practice]
└─$ scp ./example_file ognard@[redacted]:~/
ognard@[redacted]'s password: 
example_file                              100%   43     1.2KB/s   00:00 

To verify the transfer, we can connect through ssh on the remote server and check the destination directory:

┌──(ognard㉿ognard)-[~/Practice]
└─$ ssh ognard@[redacted]
ognard@[redacted]'s password: 
ognard@ubuntu-s-1vcpu-512mb-10gb-fra1-01:~$ ls -al
total 32
drwxr-x--- 3 ognard ognard 4096 Oct 30 21:22 .
drwxr-xr-x 3 root   root   4096 Oct 30 21:19 ..
-rw------- 1 ognard ognard   18 Oct 30 21:22 .bash_history
-rw-r--r-- 1 ognard ognard  220 Oct 30 21:19 .bash_logout
-rw-r--r-- 1 ognard ognard 3771 Oct 30 21:19 .bashrc
drwx------ 2 ognard ognard 4096 Oct 30 21:20 .cache
-rw-r--r-- 1 ognard ognard    0 Oct 30 21:19 .cloud-locale-test.skip
-rw-r--r-- 1 ognard ognard  807 Oct 30 21:19 .profile
-rw-rw-r-- 1 ognard ognard   43 Oct 30 21:22 example_file

Finally, the last bit of the task is to check for open ports and active connections with netstat:

┌──(ognard㉿ognard)-[~/Practice]
└─$ netstat -tuln          
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp6       0      0 :::5355                 :::*                    LISTEN     
udp        0      0 0.0.0.0:5353            0.0.0.0:*                          
udp        0      0 0.0.0.0:5355            0.0.0.0:*                          
udp        0      0 127.0.0.54:53           0.0.0.0:*                          
udp        0      0 127.0.0.53:53           0.0.0.0:*                          
udp6       0      0 :::5353                 :::*                               
udp6       0      0 :::5355                 :::*                               

Package Management


Next task objectives are to install, remove, and manage packages using dpkg and apt, and handle package dependencies.

We will start with updating the package lists and upgrading all installed packages:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo apt update && upgrade
Hit:1 http://http.kali.org/kali kali-rolling InRelease
1645 packages can be upgraded. Run 'apt list --upgradable' to see them.
...

Next, as an example for using dpkg to install a package we can do:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo dpkg -i cowsay-off_3.03+dfsg1-15_all.deb 
Selecting previously unselected package cowsay-off.
(Reading database ... 396660 files and directories currently installed.)
Preparing to unpack cowsay-off_3.03+dfsg1-15_all.deb ...
Unpacking cowsay-off (3.03+dfsg1-15) ...
dpkg: dependency problems prevent configuration of cowsay-off:
 cowsay-off depends on cowsay (>= 3.03+dfsg1-13); however:
  Package cowsay is not installed.

dpkg: error processing package cowsay-off (--install):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 cowsay-off
                                                                                                                                                            
┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo apt install -f                          
Correcting dependencies... Done 
The following packages were automatically installed and are no longer required:
  ibverbs-providers         libcephfs2  libgfxdr0      libpython3.11-dev  python3-lib2to3  python3.11-minimal
  libboost-iostreams1.83.0  libgfapi0   libglusterfs0  librados2          python3.11       samba-vfs-modules
  libboost-thread1.83.0     libgfrpc0   libibverbs1    librdmacm1t64      python3.11-dev
Use 'sudo apt autoremove' to remove them.

Installing dependencies:
  cowsay

Suggested packages:
  filters

Summary:
  Upgrading: 0, Installing: 1, Removing: 0, Not Upgrading: 1646
  1 not fully installed or removed.
  Download size: 21.4 kB
  Space needed: 94.2 kB / 44.9 GB available

Continue? [Y/n] y
Get:1 http://http.kali.org/kali kali-rolling/main amd64 cowsay all 3.03+dfsg2-8 [21.4 kB]
Fetched 21.4 kB in 0s (69.0 kB/s)  
Selecting previously unselected package cowsay.
(Reading database ... 396668 files and directories currently installed.)
Preparing to unpack .../cowsay_3.03+dfsg2-8_all.deb ...
Unpacking cowsay (3.03+dfsg2-8) ...
Setting up cowsay (3.03+dfsg2-8) ...
Setting up cowsay-off (3.03+dfsg1-15) ...
Processing triggers for man-db (2.12.1-2) ...

Since there were some dependency issues, we used sudo apt install -f to satisfy the needs.

To hold a package, we can do:

┌──(ognard㉿ognard)-[~/Practice]
└─$ echo "cowsay hold" | sudo dpkg --set-selections

To check which packages are held:

┌──(ognard㉿ognard)-[~/Practice]
└─$ dpkg --get-selections | grep hold
cowsay                                          hold

To search and show details for a package:

┌──(ognard㉿ognard)-[~/Practice]
└─$ apt search cowsay       
cowsay/kali-rolling,now 3.03+dfsg2-8 all [installed,automatic]
  configurable talking cow

cowsay-off/kali-rolling 3.03+dfsg2-8 all [upgradable from: 3.03+dfsg1-15]
  configurable talking cow (offensive cows)

presentty/kali-rolling 0.2.1-1.1 all
  Console-based presentation software

xcowsay/kali-rolling 1.6-1+b1 amd64
  Graphical configurable talking cow

...

┌──(ognard㉿ognard)-[~/Practice]
└─$ apt search cowsay       
cowsay/kali-rolling,now 3.03+dfsg2-8 all [installed,automatic]
  configurable talking cow

cowsay-off/kali-rolling 3.03+dfsg2-8 all [upgradable from: 3.03+dfsg1-15]
  configurable talking cow (offensive cows)

presentty/kali-rolling 0.2.1-1.1 all
  Console-based presentation software

xcowsay/kali-rolling 1.6-1+b1 amd64
  Graphical configurable talking cow

Finally to remove a package, we can do the following:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo apt remove cowsay         
The following packages were automatically installed and are no longer required:
  ibverbs-providers         libcephfs2  libgfxdr0      libpython3.11-dev  python3-lib2to3  python3.11-minimal
  libboost-iostreams1.83.0  libgfapi0   libglusterfs0  librados2          python3.11       samba-vfs-modules
  libboost-thread1.83.0     libgfrpc0   libibverbs1    librdmacm1t64      python3.11-dev
Use 'sudo apt autoremove' to remove them.

Changing held packages:
  cowsay

REMOVING:
  cowsay  cowsay-off

Summary:
  Upgrading: 0, Installing: 0, Removing: 2, Not Upgrading: 1645
  Freed space: 118 kB

Continue? [Y/n] y
(Reading database ... 396729 files and directories currently installed.)
Removing cowsay-off (3.03+dfsg2-8) ...
Removing cowsay (3.03+dfsg2-8) ...
Processing triggers for man-db (2.12.1-2) ...
                                                                                                                                                            
┌──(ognard㉿ognard)-[~/Practice]
└─$ sudo apt purge cowsay 
Package 'cowsay' is not installed, so not removed
The following packages were automatically installed and are no longer required:
  ibverbs-providers         libcephfs2  libgfxdr0      libpython3.11-dev  python3-lib2to3  python3.11-minimal
  libboost-iostreams1.83.0  libgfapi0   libglusterfs0  librados2          python3.11       samba-vfs-modules
  libboost-thread1.83.0     libgfrpc0   libibverbs1    librdmacm1t64      python3.11-dev
Use 'sudo apt autoremove' to remove them.

Summary:
  Upgrading: 0, Installing: 0, Removing: 0, Not Upgrading: 1645

Network Troubleshooting


The objectives of this task are to diagnose and fix network issues using various tools. First we’ll start with using ping to check connectivity with sending only 4 packets by using -c flag:

┌──(ognard㉿ognard)-[~/Practice]
└─$ ping -c 4 ognard.com
PING ognard.com (172.67.193.6) 56(84) bytes of data.
64 bytes from 172.67.193.6: icmp_seq=1 ttl=50 time=46.9 ms
64 bytes from 172.67.193.6: icmp_seq=2 ttl=50 time=46.9 ms
64 bytes from 172.67.193.6: icmp_seq=3 ttl=50 time=47.2 ms
64 bytes from 172.67.193.6: icmp_seq=4 ttl=50 time=46.5 ms

--- ognard.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3010ms
rtt min/avg/max/mdev = 46.471/46.846/47.167/0.248 ms

To trace the route to the remote server we can use traceroute. For example:

┌──(ognard㉿ognard)-[~/Practice]
└─$ traceroute ognard.com
traceroute to ognard.com (104.21.84.127), 30 hops max, 60 byte packets
 1  _gateway (192.168.64.1)  1.930 ms  1.629 ms  0.945 ms
 2  192.168.1.1 (192.168.1.1)  2.604 ms  2.558 ms  2.511 ms
 3  62.162.200.200 (62.162.200.200)  7.537 ms  7.340 ms  7.190 ms
 4  62.162.201.1 (62.162.201.1)  7.140 ms  7.093 ms  7.045 ms
 5  * * 95.158.152.41 (95.158.152.41)  7.575 ms
 6  * * *
 7  185.1.40.27 (185.1.40.27)  20.070 ms  10.072 ms  10.004 ms
 8  104.21.84.127 (104.21.84.127)  7.882 ms  9.722 ms  9.625 ms

To identify bottlenecks, we can look for significant increases in the response time for any of the hops, which can suggest that there is a network congestion. Also, as we may see from the example above, if some of the hops show *** that suggests that there may be some issues at that hop, such as packet drops or filtering.

Next objective is to check the configuration of network interfaces. We can do that with ip a or ifconfig:

┌──(ognard㉿ognard)-[~/Practice]
└─$ ip a         
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 noprefixroute 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 4e:81:77:5a:e0:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.64.11/24 brd 192.168.64.255 scope global dynamic noprefixroute eth0
       valid_lft 3005sec preferred_lft 3005sec
    inet6 fdfc:e305:f06b:6649:f343:140a:983a:5af3/64 scope global temporary dynamic 
       valid_lft 604208sec preferred_lft 85458sec
    inet6 fdfc:e305:f06b:6649:4c81:77ff:fe5a:e0ef/64 scope global dynamic mngtmpaddr noprefixroute 
       valid_lft 2591964sec preferred_lft 604764sec
    inet6 fe80::4c81:77ff:fe5a:e0ef/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

Finally, we can use netstat to display network connections, routing tables and interface statistics. For example:

┌──(ognard㉿ognard)-[~/Practice]
└─$ netstat -tuln          
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:5355            0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.54:53           0.0.0.0:*               LISTEN     
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN     
tcp6       0      0 :::5355                 :::*                    LISTEN     
udp        0      0 0.0.0.0:5353            0.0.0.0:*                          
udp        0      0 0.0.0.0:5355            0.0.0.0:*                          
udp        0      0 127.0.0.54:53           0.0.0.0:*                          
udp        0      0 127.0.0.53:53           0.0.0.0:*                          
udp6       0      0 :::5353                 :::*                               
udp6       0      0 :::5355                 :::*                               

Flags used are -t to display TCP, -u to display UDP, -l to display listening and -n to display numerical connections. These flags are helpful for seeing open ports, active connections and statistics.

To see the routing tables, we can use -r flag:

┌──(ognard㉿ognard)-[~/Practice]
└─$ netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         _gateway        0.0.0.0         UG        0 0          0 eth0
192.168.64.0    0.0.0.0         255.255.255.0   U         0 0          0 eth0

And to display interface statistics, we can use -I flag:

┌──(ognard㉿ognard)-[~/Practice]
└─$ netstat -i
Kernel Interface table
Iface             MTU    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
eth0             1500       95      0      0 0           139      0      0      0 BMRU
lo              65536       88      0      0 0            88      0      0      0 LRU

Files Manipulation


The objectives for the final task are to perform complex file manipulation tasks, including searching, sorting and editing files. We’ll start with generating a file that will be used for this purpose:

┌──(ognard㉿ognard)-[~/Practice]
└─$ seq 1 1000 | shuf > 1000_random_numbers.txt

seq will generate numbers from 1 to 1000, one number per line and shuf will shuffle, or randomize the order of the lines. After that we will store the output to a file.

We will modify the last lines of the file, just for the purpose of the upcoming objectives and making the process more clear. To do that we can use nano and just add the last three (620) lines like this:

...
70
300
18
620
620
620
620
This line will serve for grep objective.

Now, to search for specific patters in this file, we can use grep:

┌──(ognard㉿ognard)-[~/Practice]
└─$ grep "objective" 1000_random_numbers.txt
This line will serve for grep objective.

This will show very line where the pattern (i.e. “objective”) exists.

Next we will use awk for processing and formatting the file. For example, we can set a prefix for each line so that it will mark the line number and store it to a new file:

┌──(ognard㉿ognard)-[~/Practice]
└─$ awk '{print "Line " NR ": " $0}' 1000_random_numbers.txt > prefixed_1000_random_numbers.txt

Sample of the newly generated file:

Line 998: 300
Line 999: 18
Line 1000: 620
Line 1001: 620
Line 1002: 620
Line 1003: 620
Line 1004: This line will serve for grep objective.

For the next objective, we will sort the contents and remove the duplicates in the original file:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sort -n 1000_random_numbers.txt| uniq > sorted_1000_random_numbers.txt

sort will organize the lines in ascending order by default and uniq will remove duplicate lines (this only works if the file is already sorted), and finally it will store the output to a new file.

-n flag for sort will set the sorting in numeric order instead of alphabetical.

Example output:

989
990
991
992
993
994
995
996
997
998
999
1000

And if we use grep again to search for the duplicate 620, we can see that there are no more duplicates of it:

┌──(ognard㉿ognard)-[~/Practice]
└─$ grep "620" sorted_1000_random_numbers.txt 
620

Next objective is to perform substitution in the file. For this purpose we will use the text line we’ve added earlier in the file:

┌──(ognard㉿ognard)-[~/Practice]
└─$ sed 's/grep/substitution/g' 1000_random_numbers.txt
...
620
620
This line will serve for substitution objective.

Note that the word “grep” was changed to “substitution”. Of course, we could have stored this output to a new file as well.

And finally, the last objective is to compress the text file using gzip:

┌──(ognard㉿ognard)-[~/Practice]
└─$ gzip 1000_random_numbers.txt 

And then we can decompress the file:

┌──(ognard㉿ognard)-[~/Practice]
└─$ gunzip 1000_random_numbers.txt.gz