Checking Drive Status on CentOS

SMART monitoring is not included in the default minimal installation; you will have to install it manually:
yum install smartmontools

Enable the monitoring service; uncomment and edit the various failure/prefailure modes you would like to know about and add your e-mail address to the config file to be notified of errors:
systemctl start smartd ; systemctl enable smartd nano /etc/smartmontools/smartd.conf

Get the basic status of a drive (replace sda accordingly)
smartctl -H /dev/sda

Run self-tests. A short self-test typically completes in under one or two minutes. A full test will depend on the specifications of the drive.
smartctl --test=short /dev/sda smartctl --test=long /dev/sda

You can monitor test progress using the same command you will use to view the results and nitty-gritty:
smartctl -a /dev/sda

Don't be trown off if the values seem out of alignment with the listed thresholds; SMART values are notoriously proprietary. Your main concern is that you receive a status of PASSED

smartctl 7.0 2018-12-30 r4883 [x86_64-linux-4.9.199-35.el7.x86_64] (local build) Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org === START OF INFORMATION SECTION === Model Family: Seagate Barracuda 7200.14 (AF) Device Model: ST1000DM003-9YN162 Serial Number: ******** LU WWN Device Id: * ****** ******** Firmware Version: CC82 User Capacity: 1,000,204,886,016 bytes [1.00 TB] Sector Sizes: 512 bytes logical, 4096 bytes physical Rotation Rate: 7200 rpm Device is: In smartctl database [for details use: -P show] ATA Version is: ATA8-ACS T13/1699-D revision 4 SATA Version is: SATA 3.0, 6.0 Gb/s (current: 3.0 Gb/s) Local Time is: Wed Jan 8 03:31:41 2020 EST SMART support is: Available - device has SMART capability. SMART support is: Enabled === START OF READ SMART DATA SECTION === SMART overall-health self-assessment test result: PASSED General SMART Values: Offline data collection status: (0x00) Offline data collection activity was never started. Auto Offline Data Collection: Disabled. Self-test execution status: ( 242) Self-test routine in progress... 20% of test remaining. Total time to complete Offline data collection: ( 584) seconds. Offline data collection capabilities: (0x73) SMART execute Offline immediate. Auto Offline data collection on/off support. Suspend Offline collection upon new command. No Offline surface scan supported. Self-test supported. Conveyance Self-test supported. Selective Self-test supported. SMART capabilities: (0x0003) Saves SMART data before entering power-saving mode. Supports SMART auto save timer. Error logging capability: (0x01) Error logging supported. General Purpose Logging supported. Short self-test routine recommended polling time: ( 1) minutes. Extended self-test routine recommended polling time: ( 118) minutes. Conveyance self-test routine recommended polling time: ( 2) minutes. SCT capabilities: (0x3085) SCT Status supported. SMART Attributes Data Structure revision number: 10 Vendor Specific SMART Attributes with Thresholds: ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE UPDATED WHEN_FAILED RAW_VALUE 1 Raw_Read_Error_Rate 0x000f 114 099 006 Pre-fail Always - 68174856 3 Spin_Up_Time 0x0003 097 097 000 Pre-fail Always - 0 4 Start_Stop_Count 0x0032 100 100 020 Old_age Always - 401 5 Reallocated_Sector_Ct 0x0033 100 100 036 Pre-fail Always - 0 7 Seek_Error_Rate 0x000f 087 060 030 Pre-fail Always - 582493393 9 Power_On_Hours 0x0032 056 056 000 Old_age Always - 39397 10 Spin_Retry_Count 0x0013 100 100 097 Pre-fail Always - 0 12 Power_Cycle_Count 0x0032 100 100 020 Old_age Always - 185 183 Runtime_Bad_Block 0x0032 100 100 000 Old_age Always - 0 184 End-to-End_Error 0x0032 100 100 099 Old_age Always - 0 187 Reported_Uncorrect 0x0032 100 100 000 Old_age Always - 0 188 Command_Timeout 0x0032 100 099 000 Old_age Always - 0 0 2 189 High_Fly_Writes 0x003a 090 090 000 Old_age Always - 10 190 Airflow_Temperature_Cel 0x0022 077 060 045 Old_age Always - 23 (Min/Max 22/23) 191 G-Sense_Error_Rate 0x0032 100 100 000 Old_age Always - 0 192 Power-Off_Retract_Count 0x0032 100 100 000 Old_age Always - 158 193 Load_Cycle_Count 0x0032 001 001 000 Old_age Always - 248161 194 Temperature_Celsius 0x0022 023 040 000 Old_age Always - 23 197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - 0 198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - 0 199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age Always - 0 240 Head_Flying_Hours 0x0000 100 253 000 Old_age Offline - 33200h+58m+57.958s 241 Total_LBAs_Written 0x0000 100 253 000 Old_age Offline - 782296782482 242 Total_LBAs_Read 0x0000 100 253 000 Old_age Offline - 4312618627972 SMART Error Log Version: 1 No Errors Logged SMART Self-test log structure revision number 1 Num Test_Description Status Remaining LifeTime(hours) LBA_of_first_error # 1 Short offline Self-test routine in progress 20% 39397 - SMART Selective self-test log data structure revision number 1 SPAN MIN_LBA MAX_LBA CURRENT_TEST_STATUS 1 0 0 Not_testing 2 0 0 Not_testing 3 0 0 Not_testing 4 0 0 Not_testing 5 0 0 Not_testing Selective self-test flags (0x0): After scanning selected spans, do NOT read-scan remainder of disk. If Selective self-test is pending on power-up, resume after 0 minute delay.

Port Knocking

Port knocking is a fun tactic for exposing otherwise hidden services to a client that can present a shared secret in the form of a correct series of particular packets. Since the server end of the scheme is typically implemented with Netfilter the number of ways the authentication can be complicated is limited only by Netfilter's capacity to filter packets. For example you could choose to implement a scheme that requires packets of various sizes, header configurations or payloads. In its simplest and most common form the client sends a tcp SYN packet to a series of ports at the destination address in a specific sequence known only to the client and server. This allows an authorized user to gain access from virtually any machine equipped with any number of TCP based, port-configurable clients including telnet, avoiding the inherent problems of requiring specialty software or custom scripts.

#!/bin/sh # # http://foxpa.ws/port-knocking/ # # Uncomment LOG rules to monitor client activity via syslog. PORT1=1111 PORT2=2222 PORT3=3333 PORT4=4444 PROTECTED=22 TIMEOUT=60 iptables -N KNOCKP1 iptables -A KNOCKP1 -m recent --name KNOCKP1 --set #iptables -A KNOCKP1 -j LOG --log-prefix "KNOCK PHASE1: " iptables -N KNOCKP2 iptables -A KNOCKP2 -m recent --name KNOCKP1 --remove iptables -A KNOCKP2 -m recent --name KNOCKP2 --set #iptables -A KNOCKP2 -j LOG --log-prefix "KNOCK PHASE2: " iptables -N KNOCKP3 iptables -A KNOCKP3 -m recent --name KNOCKP2 --remove iptables -A KNOCKP3 -m recent --name KNOCKP3 --set #iptables -A KNOCKP3 -j LOG --log-prefix "KNOCK PHASE3: " iptables -N KNOCKP4 iptables -A KNOCKP4 -m recent --name KNOCKP3 --remove iptables -A KNOCKP4 -m recent --name KNOCKP --set #iptables -A KNOCKP4 -j LOG --log-prefix "KNOCK PHASE4: " iptables -N KNOCK iptables -A KNOCK -p tcp -m tcp --dport $PORT1 -m conntrack --ctstate NEW -j KNOCKP1 iptables -A KNOCK -p tcp -m tcp --dport $PORT2 -m conntrack --ctstate NEW -m recent --rcheck --seconds $TIMEOUT --reap --name KNOCKP1 -j KNOCKP2 iptables -A KNOCK -p tcp -m tcp --dport $PORT3 -m conntrack --ctstate NEW -m recent --rcheck --seconds $TIMEOUT --reap --name KNOCKP2 -j KNOCKP3 iptables -A KNOCK -p tcp -m tcp --dport $PORT4 -m conntrack --ctstate NEW -m recent --rcheck --seconds $TIMEOUT --reap --name KNOCKP3 -j KNOCKP4 iptables -A INPUT ! -s -j KNOCK iptables -A INPUT -p tcp -m tcp -dport $PROTECTED -m conntrack --ctstate NEW -m recent --rcheck --seconds $TIMEOUT --name KNOCK --reap -j ACCEPT iptables -A INPUT -p tcp -m tcp -dport $PROTECTED -m conntrack --ctstate NEW -j REJECT

We can use netcat or nmap to send our knocks:
$ nc -z -w1 hostname port $ nmap -Pn --host_timeout 1 --max-retries 0 -p port hostname

It's easy to create a one-liner using shell loops:
$ for x in 1111 2222 3333 4444; do nc -z -w1 hostname $x && sleep 1; done && ssh hostname

You can add this to your OpenSSH client configuration at ~/.ssh/config (create the file if it does not exist) so that it runs every time you attempt to connect to host.alias:
Host host.alias HostName hostname ProxyCommand sh -c "for x in 1111 2222 3333 4444; do nc -z -w1 %h $x; done && nc %h %p"

Mitigating Connection Flooding/DoS and Brute Force Attacks with Netfilter (iptables)

The simplest thing we can do to throw off an automated attack is to initially throw a failure state. While a lot of legitimate client software is designed to be tolerant (i.e. retry the connection if the first attempt fails) attackers have no reason to consider user friendliness when they design their bots and poking at a seemingly unresponsive host is a waste of time that could be better spent on the next target.

We can use Netfilter to introduce a crude delay (a single knock port knock, if you will...) to new connections and even if our attacker sticks around intentional delays severely limit the effectiveness of brute force cracking:
iptables -A INPUT -p tcp -i eth0 -m conntrack --ctstate NEW --dport 22 -m recent --update --seconds 15 -j DROP iptables -A INPUT -p tcp -i eth0 -m conntrack --ctstate NEW --dport 22 -m recent --set -j ACCEPT

We can be more ideologically correct by monitoring new connections and drop them when there have been too many in a given time frame. By using two thresholds we can immediately block fast-acting attackers before they get more than a few tries in and take out rate-limited bots that generate a lot of attempts over a longer time without (hopefully) intercepting users that have forgotten their password:
iptables -N IN_SSH iptables -A INPUT -p tcp --dport ssh -m conntrack --ctstate NEW -j IN_SSH iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 3 --seconds 10 -j DROP iptables -A IN_SSH -m recent --name sshbf --rttl --rcheck --hitcount 10 --seconds 1800 -j DROP iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT

We may need to limit the number of connections to a service, particularly if establishing a new connection is a very resource-intense proposition.
iptables -A INPUT -p tcp --dport 1935 -m conntrack --ctstate NEW -m limit --limit 5/second --limit-burst 20 -j ACCEPT iptables -A input -p tcp --dport 1935 -m conntrack --ctstate NEW -j DROP

The limit module applies to packets in general and wouldn't apply to whole connections if we weren't specifying the NEW state; as the name implies it's more suited to rate limiting the speed of traffic flows. Our limit rule also doesn't care how many connections are coming from each individual address. A better way to implement whole-connection limiting might be with the connlimit module:
iptables -A INPUT -p tcp -m tcp --dport 1935 -m connlimit --connlimit-above 5 --connlimit-mask 32 -j REJECT --reject-with tcp-reset
--connlimit-mask refers to the subnet mask, meaning this rule will apply to indivudal IPs when set to 32. We can broaden it to apply to an entire class C subnet by changing it to 24.

MAC Address Filtering with iptables

Source and destination MAC addresses are included in the header of every Ethernet (and by extension, WiFi) frame. As the name implies, iptables is generally intended for use with Layer 3 protocols and as such has limited support for dealing with MAC addresses. Layer 2 filtering is more properly the domain of arptables and ebtables. That being said, iptables does have a limited 'mac' module that lets us filter by --mac-source which lets us do some neat things to local packets received on Ethernet/WiFi interfaces.

For example, you may wish to ignore all traffic from a particular MAC on your network:
iptables -A INPUT -m mac --mac-source 12:34:56:78:9a:bc -j DROP

Or flip it around with the ! operator and discard all traffic from any device on your local network other than your upstream gateway:
iptables -A INPUT -m mac ! --mac-source 12:34:56:78:9a:bc -j DROP

You might find it handy to allow SSH access only to one device on a particular interface, avoiding having to specify any IP addresses which may be ideal on a DHCP-configured or frequently changing LAN:
iptables -A INPUT -i eth0 --dport 22 -m mac ! --mac-source 12:34:56:78:9a:bc -j REJECT

If you want to set up an ACL similar to a hotspot whitelist/blacklist it may be easier to maintain a list of addresses in a text file:
for MAC in `cat /path/to/whitelist.txt`; do iptables -A FORWARD -i eth0 -o eth1 -m mac --mac-source $MAC -j ACCEPT done iptables -P FORWARD DROP
It is possible to defend against ARP poisoning/spoofing by taking this one step further and dropping all traffic coming from IPs that don't match their proper MAC address if you maintain a table of every device you wish to communicate with, statically configured their IP addresses or set up static leases in your DHCP server.

It should be noted that since the only filter available to us is the MAC source address we can only create useful filters in the INPUT, FORWARD and PREROUTING chains.

You can find a device's MAC address locally using ifconfig:
# ifconfig eth0 eth0: flags=4163 mtu 1500 inet netmask broadcast ether 12:34:56:78:9a:bc txqueuelen 1000 (Ethernet) RX packets 1035457 bytes 733963916 (699.9 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 1183022 bytes 909429182 (867.2 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

...or ip:
# ip addr show eth0 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 12:34:56:78:9a:bc brd ff:ff:ff:ff:ff:ff

...or ethtool:
# ethtool -P eth0 Permanent address: 12:34:56:78:9a:bc

You can find a remote device's MAC address by running arping:
# arping ARPING 60 bytes from 12:34:56:78:9a:bc ( index=0 time=775.829 usec ...

Alternatively, since arping is not always available (or you may be using a Windows machine...) you can send a conventional ICMP echo request to the device to ensure a record is entered into your local ARP table and obtain its address there:
# ping PING ( 56(84) bytes of data. 64 bytes from icmp_seq=1 ttl=255 time=1.95 ms ^C --- ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 1.947/1.947/1.947/0.000 ms # arp -a | grep ? ( at 12:34:56:78:9a:bc [ether] on eth0

Shell Scripting Variable Cheat Sheet

There are myriad implementations of the common UNIX shell. Often these shells are syntactically compatible and the following rules tend to apply when dealing with variables:

  • Names must be alphanumeric and may not start with a number.
  • Names are case sensitive and conventionally upper-case.
  • There may be no whitespace between the variable name, assignment operator and value when a variable is set.
  • Variable names begin with the $ character when referred to in read operations; this character is omitted when they are set.
  • Variables are scoped within the script they are defined in, unless they are exported.
  • Variables may be flagged readonly upon initialization.
  • Command substitution assigns the output of commands contained in $() at the time the variable is set.
  • Variables may be unset via the unset command.

MYVAR=1 COMPLEXVAR="A string containing spaces." readonly ROVAR="This value can not be changed later." CSVAR=$( ls /var | wc -l )
Special variables dynamically made available at runtime:

  • $0 - The name of the script.
  • $1 - $9 - The first 9 arguments to the script.
  • $# - How many arguments were passed to the script.
  • [email protected] - All the arguments supplied to the script.
  • $? - The exit status of the most recently run process.
  • $$ - The process ID of the current script.

You can see additional variables currently available to you by issuing env or export without arguments at the command line or from inside your script.
$ env SHELL=/bin/bash LANGUAGE=en_US.UTF-8 NO_AT_BRIDGE=1 PWD=/home LOGNAME=username XDG_SESSION_TYPE=tty HOME=/home/username LANG=en_CA.UTF-8 XDG_SESSION_CLASS=user TERM=xterm-256color USER=username SHLVL=1 XDG_SESSION_ID=c16 XDG_RUNTIME_DIR=/run/user/1000 LC_ALL=en_US.UTF-8 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus MAIL=/var/mail/username SSH_TTY=/dev/pts/4 TEXTDOMAIN=Linux-PAM _=/usr/bin/env

Special variables available to BASH scripts:

  • $EPOCHSECONDS - Each time this parameter is referenced it expands to the number of seconds since the Unix Epoch. (eg: 1577354438)
  • $EPOCHREALTIME - Same as $EPOCHSECONDS but includes microseconds. (eg: 1577354438.788521)
  • $SECONDS - Expands to the number of seconds since the shell was started. Assignment to this variable resets the count to the value assigned, and the expanded value becomes the value assigned plus the number of seconds since the assignment.
  • $RANDOM - Each time this parameter is referenced, a random integer between 0 and 32767 is generated. Assigning a value to this variable seeds the random number generator.
  • $LINENO - Returns the current line number in the Bash script.
  • $MACHTYPE - A string that fully describes the system type on which Bash is executing, in the standard GNU cpu-company-system format. (eg: arm-unknown-linux-gnueabihf)
  • $OSTYPE - A string describing the operating system Bash is running on. (eg: linux-gnueabihf)

Additional BASH-specific variables can be found at the BASH Reference Manual.

You can find a more in-depth look at variable syntax and example use cases at https://ryanstutorials.net/bash-scripting-tutorial/bash-variables.php.