This is a short guide for protecting internet facing Linux servers from attack. The guide assumes Red Hat 7, but should mostly work with other distrubutions. There are three components to the solution.
The IPdeny Project was founded to offer up to date and ready-to-go country block zone files allocated by regional registries (RIR's). Their main goal is to publish all allocated IP's into single country files in CIDR format. You can freely use their IP data files to block certain countries or regions using your packet filters or any custom software.
I chose to create a shell script, loadzones.pl, to load the firewall rules as well as keep the zone files up to date.
Fail2ban scans log files (e.g. /var/log/apache/error_log) and bans IPs that show malicious signs like -- too many password failures, seeking for exploits, etc. Generally Fail2Ban is then used to update firewall rules to reject the IP addresses for a specified amount of time, although any arbitrary other action (e.g. sending an email) could also be configured. Out of the box Fail2Ban comes with filters for various services (apache, courier, ssh, etc).
Fail2Ban is able to reduce the rate of incorrect authentication attempts however it cannot eliminate the risk that weak authentication presents. Configure services to use only two factor or public/private authentication mechanisms if you really want to protect services.
Autoban is a shell script used to permanently block malicious IPs. It also adds an entry to the autoban database that can be used for attack analysis.
{ "iptables" : false }
So I believe the most effective use of firewalld is:
For example, assuming default zone is public and has no open ports, add source and port range to "work" zone:
# firewall-cmd --zone=work --add-source=192.168.0.0.24 # firewall-cmd --zone=work --add-port=8080-8090/tcp
Now check the active zones (default zone is always active):
# firewall-cmd --get-active-zones
You will get:
work sources: 192.168.0.0/24
So "work" zone rules will apply to the particular subnet. Of courxe use --permanent option to make the behaviour stick.
In turn any ports or services you have in "public" (default) zone will apply to all interfaces and source addresses.
# firewall-cmd --list-all-zones public (default) interfaces: sources: services: port: masquerade: no forward-ports: icmp-blocks: rich rules: work (active) interfaces: sources: 192.168.0.0/24 services: dhcpv6-client ipp-client ssh port: 8080-8090/tcp masquerade: no forward-ports: icmp-blocks: rich rules:
The same system works for interfaces. Say by adding "ens3" to "work" zone:
# firewall-cmd --zone=work --add-interface=ens3
For KVM servers make the following change to allow the free flow of traffic to virtual clients.
# firewall-cmd --permanent --remove-interface br0 --zone=public # firewall-cmd --permanent --add-interface br0 --zone=trusted # firewall-cmd --reload
The integrated PKI Service is provided via the Dogtag project. PKI signs and publishes certificates for FreeIPA hosts and services. It also provides CRL and OCSP services for all software validating the published certificate. FreeIPA management framework provides API to request, show and find certificates.
As the certificates used by FreeIPA client hosts and services have limited validity, the infrastructure also needs to handle reliable renewal of the certificates. For that purpose, a Certmonger daemon is running on all clients and handles the renewal in a transparent way for the services using it.
FreeIPA server PKI can be configured in several configurations to fit into potentially existing PKI infrastructure
FreeIPA clients and their services are neither expected nor allowed to communicate with PKI directly. They are supposed to utilize the FreeIPA server API instead, using the standard Kerberos authentication. FreeIPA web service then validates the request and passes it to the PKI service, authenticating with an own agent certificate (ipaCert stored in /etc/httpd/alias/)
Certificate can be requested either manually by a privileged user who is then able to request it for any chosen hostname (cn) or by the host itself, which can request a certificate for it's own hostname, ideally via Certmonger.
Manual certificate requests
On a FreeIPA client, run the following commands to request a new certificate
which can then be used by a mod_nss Apache module to secure a HTTPS traffic
with a certificate published by FreeIPA CA:
# ipa service-add HTTP/`hostname`
# mkdir -p /etc/httpd/alias; cd /etc/httpd/alias # certutil -N -d .
# chown :apache *.db && chmod g+rw *.db # semanage fcontext -a -t cert_t "/etc/httpd/alias(/.*)?" # restorecon -FvvR /etc/httpd/alias/
# certutil -A -d . -n 'EXAMPLE.COM IPA CA' -t CT,, -a < /etc/ipa/ca.crt
# certutil -R -d . -a -g 2048 -s CN=`hostname`,O=EXAMPLE.COM > web.csr # ipa cert-request --principal=HTTP/`hostname` web.csr # ipa cert-show $SERIAL_NUMBER --out=web.crt # certutil -A -d . -n Server-Cert -t u,u,u -i web.crt
Optionally show and validate the certificate
# certutil -L -d . -n Server-Cert # certutil -V -u V -d . -n Server-Cert
Obviously, this procedure has a disadvantage of the certificate not being tracked by the Certmonger and thus not being automatically renewed before it's validity ends.
Automated certificate requests with Certmonger To request a new (HTTP) certificate for a FreeIPA client, the procedure is slightly easier. The biggest benefit is that the certificate is automatically renewed before the validation ends:
# ipa service-add HTTP/`hostname`
# mkdir -p /etc/httpd/alias; cd /etc/httpd/alias # certutil -N -d .
# echo $PIN > pwdfile.txt # chmod o-rwx pwdfile.txt
# chown :apache *.db && chmod g+rw *.db # semanage fcontext -a -t cert_t "/etc/httpd/alias(/.*)?" # restorecon -FvvR /etc/httpd/alias/
# certutil -A -d . -n 'EXAMPLE.COM IPA CA' -t CT,, -a < /etc/ipa/ca.crt
# ipa-getcert request -d /etc/httpd/alias -n Server-Cert -K HTTP/`hostname`
-N CN=`hostname`,O=EXAMPLE.COM -g2048 -p /etc/httpd/alias/pwdfile.txt
# ipa-getcert list -d /etc/httpd/alias/ -n Server-Cert
# certutil -L -d . -n Server-Cert # certutil -V -u V -d . -n Server-Cert
# How to sign your custom RPM package with GPG key # Step: 1 # Generate gpg key pair (public key and private key) # # You will be prompted with a series of questions about encryption. # Simply select the default values presented. You will also be asked # to create a Real Name, Email Address and Comment (comment optional). # # If you get the following response: # ----------------------------------------------------------------------- # We need to generate a lot of random bytes. It is a good idea to perform # some other action (type on the keyboard, move the mouse, utilize the # disks) during the prime generation; this gives the random number # generator a better chance to gain enough entropy. # ----------------------------------------------------------------------- # Open up a separate terminal, ssh into your server and run this command: # ls -R / gpg --gen-key # Step: 2 # Verify your gpg keys were created gpg --list-keys # Step: 3 # Export your public key from your key ring to a text file. # # You will use the information for Real Name and Email you used to # create your key. I used Fernando Aleman and faleman@email.com gpg --export -a 'Fernando Aleman' > RPM-GPG-KEY-faleman # Step: 4 # Import your public key to your RPM DB # # If you plan to share your custom built RPM packages with others, make sure # to have your public key file available online so others can verify RPMs sudo rpm --import RPM-GPG-KEY-faleman # Step: 5 # Verify the list of gpg public keys in RPM DB rpm -q gpg-pubkey --qf '%{name}-%{version}-%{release} --> %{summary}\n' # Step: 6 # Configure your ~/.rpmmacros file # # You can use the following command to edit if you are on the server: # vi ~/.rpmmacros # # %_signature => This will always be gpg # %_gpg_path => Enter full path to .gnupg in your home directory # %_gpg_name => Use the Real Name you used to create your key # %_gpbin => run `which gpg` (without ` marks) to get full path %_signature gpg %_gpg_path /root/.gnupg %_gpg_name Fernando Aleman %_gpgbin /usr/bin/gpg # Step: 7 # Sign your custom RPM package # # You can sign each RPM file individually: rpm --addsign git-1.7.7.3-1.el6.x86_64.rpm # Or you can `cd` into your RPMS folder and sign them all: rpm --addsign *.rpm # Step: 8 # Check the signature to make sure it was signed # # Watch for 'gpg OK' as in this example: # git-1.7.7.3-1.el6.x86_64.rpm: (sha1) dsa sha1 md5 gpg OK rpm --checksig git-1.7.7.3-1.el6.x86_64.rpm # Tip! # Sign package during build # # To sign a package while it's being built, simply add '--sign' rpmbuild -ba --sign git.spec
Certs from Let's Encrypt have a maximum validity duration of 90 days. There are many plugins available for Certbot which can be used to automate the certificate update process. Unfortunately, my experience with these plugins has not been favorable. At the time of this writting, a plugin for Apache servers that use mod_nss is not available. Also, the available plugins to not work well with Apache security modules such as authnz_pam, mod_security and mod_evasive. I therefore decided, at least for now, to manually update certificates using the following process. The instructions apply to RedHat/CentOS 7 & 8 systems.
Install certbot from the EPEL repository, if not already installed.
cd /etc/letsencrypt/live/<DOMAIN>
systemctl stop httpd.service
certbot certonly --standalone --preferred-challenges http \ -d <DOMAIN1> -d <DOMAIN2>The certificate and chain will be saved at:
/etc/letsencrypt/live/<DOMAIN>/
openssl pkcs12 -export -in cert.pem --inkey privkey.pem \ -certfile chain.pem -password file: <PWFILE> -out cert.pfx \ -name "Server-Cert"
certutil -F -d /etc/httpd/alias -n Server-Cert
pk12util -d /etc/httpd/alias/ -w <PWFILE> -i cert.pfx
systemctl start httpd.service
If you’d like to obtain a certificate running certbot on a machine other than your target webserver or perform the steps for domain validation yourself, you can use the manual plugin. While hidden from the UI, you can use the plugin to obtain a certificate by specifying certonly and --manual on the command line. This requires you to copy and paste commands into another terminal session, which may be on a different computer.
When using the dns challenge, certbot will ask you to place a TXT DNS record with specific contents under the domain name consisting of the hostname for which you want a certificate issued, prepended by _acme-challenge.
sudo certbot certonly --manual --preferred-challenge dns -d *.ohares.us
Instruction for recieving or renewing a GeoTrust certificate from No-IP.com.
Note, the No-IP website does not include the ability to "renew" a certificate. Instead you wait until your existing cert is about to expire, and then purchase a new one. No-IPs' maximum certificate term is two years.
If you don't already have a private key, create a private key and
signing request as follows:
openssl req -out www.ohares.us.csr -new -newkey rsa:2048 -nodes \ -keyout www.ohares.us.key
If you already have a private key, generate a certificate signing request as follows:
openssl req -new -out www.ohares.us.csr -key www.ohares.us.key \ -config www.ohares.us.cnf -sha256
To the get the CSR signed,go to https://my.noip.com/, and follow their instructions.
After you download your new certificate, it must be converted to NSSDB
format for import
openssl pkcs12 -export -in www.ohares.us.crt -inkey www.ohares.us.key \ -certfile GeoTrust.crt -password file:pw.txt -out cert.pfx \ -name 'Server-Cert'
Remove existing cert and key
certutil -F -d /etc/httpd/alias -n Server-Cert
Import new pkcs12 file
pk12util -d /etc/httpd/alias -w pw.txt -i cert.pfx
Restart httpd service
systemctl start httpd.service
Cannot Activate RHEL Server with Free Developer Subscription
I signed up for the free Red Hat developer license, but I can't activate my RHEL server install. My account says I have an active Red Hat Enterprise Linux Developer Suite subscription, but when I go to attach my system to it, I get a message saying I don't have any matching subscriptions.
It happens randomly every now and then ... You should be able to solve it by executing :
sudo subscription-manager remove --all sudo subscription-manager unregister sudo subscription-manager clean sudo subscription-manager register sudo subscription-manager refresh sudo subscription-manager attach --auto
This guide shows how to remove a failed hard drive from a Linux RAID1 array (software RAID), and how to add a new hard disk to the RAID1 array without losing data.
In this example I have two hard drives, /dev/sda and /dev/sdb, with the partitions /dev/sda1 and /dev/sda2 as well as /dev/sdb1 and /dev/sdb2.
/dev/sda1 and /dev/sdb1 make up the RAID1 array /dev/md0.
/dev/sda2 and /dev/sdb2 make up the RAID1 array /dev/md1.
/dev/sdb has failed, and we want to replace it.
If a disk has failed, you will probably find a lot of error messages in the log files, e.g. /var/log/messages or /var/log/syslog.
You can also run
cat /proc/mdstat
and instead of the string [UU] you will see [U_] if you have a degraded RAID1 array.
To remove /dev/sdb, we will mark /dev/sdb1 and /dev/sdb2 as failed and remove them from their respective RAID arrays (/dev/md0 and /dev/md1).
First we mark /dev/sdb1 as failed:
server1:~# mdadm --manage /dev/md0 --fail /dev/sdb1
Result:
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] sdb1[2](F) 24418688 blocks [2/1] [U_] md1 : active raid1 sda2[0] sdb2[1] 24418688 blocks [2/2] [UU] unused devices:Then we remove /dev/sdb1 from /dev/md0:
server1:~# mdadm --manage /dev/md0 --remove /dev/sdb1 mdadm: hot removed /dev/sdb1And "cat /proc/mdstat" should show this:
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] 24418688 blocks [2/1] [U_] md1 : active raid1 sda2[0] sdb2[1] 24418688 blocks [2/2] [UU] unused devices:Now we do the same steps again for /dev/sdb2 (which is part of /dev/md1): server1:~# mdadm --manage /dev/md1 --fail /dev/sdb2
Result:
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] 24418688 blocks [2/1] [U_] md1 : active raid1 sda2[0] sdb2[2](F) 24418688 blocks [2/1] [U_] unused devices:Repeat for /dev/sdb2
server1:~# mdadm --manage /dev/md1 --remove /dev/sdb2 mdadm: hot removed /dev/sdb2Result:>
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] 24418688 blocks [2/1] [U_] md1 : active raid1 sda2[0] 24418688 blocks [2/1] [U_] unused devices:
Then power down the system and replace the old /dev/sdb hard drive with a new one
(it must have at least the same size as the old one - if it's only a few MB
smaller than the old one then rebuilding the arrays will fail).
sfdisk -d /dev/sda | sfdisk /dev/sdbYou can run
fdisk -lto check if both hard drives have the same partitioning now. Next we add /dev/sdb1 to /dev/md0 and /dev/sdb2 to /dev/md1: mdadm --manage /dev/md0 --add /dev/sdb1
server1:~# mdadm --manage /dev/md0 --add /dev/sdb1 mdadm: re-added /dev/sdb1 server1:~# mdadm --manage /dev/md1 --add /dev/sdb2 mdadm: re-added /dev/sdb2Now both arays (/dev/md0 and /dev/md1) will be synchronized. Run
cat /proc/mdstatto see when it's finished. During the synchronization the output will look like this:
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] sdb1[1] 24418688 blocks [2/1] [U_] [=>...................] recovery = 9.9% (2423168/24418688) finish=2.8min speed=127535K/sec md1 : active raid1 sda2[0] sdb2[1] 24418688 blocks [2/1] [U_] [=>...................] recovery = 6.4% (1572096/24418688) finish=1.9min speed=196512K/sec unused devices:When the synchronization is finished, the output will look like this:
server1:~# cat /proc/mdstat Personalities : [linear] [multipath] [raid0] [raid1] [raid5] [raid4] [raid6] [raid10] md0 : active raid1 sda1[0] sdb1[1] 24418688 blocks [2/2] [UU] md1 : active raid1 sda2[0] sdb2[1] 24418688 blocks [2/2] [UU] unused devices:That's it, you have successfully replaced /dev/sdb!