Linux

  • Kill all sub-processes of a bash script with SIGINT (CTRL-C)

    If you have a bash script running with a very long loop (or infinite one) and starting multiple background processes, you will notice that when you stop the main process, all the child processes will continue to live until you restart the machine or you kill them one by one.

    So that you can easily stop all the sub-processes when you stop the main process, you just need to use the trap function to capture the event and stop all the jobs in the background.

    Here’s an example of code, executing 2 infinite functions:

    #!/bin/bash
    
    # Get number of seconds of uptime
    func_1() { 
        while true
        do
            echo "Press Ctrl-C to stop me even if I'm a child..."
            sleep 10
        done
    }
    
    func_2() {
        while true
        do
            echo `date`
            sleep 3
        done
    }
    
    #####################
    ### MAIN PROGRAM ####
    #####################
    
    declare -a pids
    declare -a functions=(func_1 func_2)
    
    # Parse list of devices
    i=0
    for func in "${functions[@]}"
    do
        ${func} &
        pids[${i}]=$!
        i=$((i+1))
    done
    
    trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
    
    # wait for all pids
    for pid in ${pids[*]}; do
        wait $pid
    done

    Output of the script until we hit Ctrl-C:

    # bash stop_proc.sh
    Press Ctrl-C to stop me even if I'm a child…
    Tue Nov 14 22:28:55 UTC 2022
    Tue Nov 14 22:28:58 UTC 2022
    Tue Nov 14 22:29:01 UTC 2022
    Tue Nov 14 2:29:04 UTC 2022
    Press Ctrl-C to stop me even if I'm a child…
    Tue Nov 14 22:29:07 UTC 2022
    Tue Nov 14 22:29:10 UTC 2022
    ^CTerminated
  • Install pip packages offline

    Sometimes you might need to download some pip packages offline on a device with no Internet access. This is entirely feasible through multiple ways.

    Install a single package:

    You first need to download that package on a device with Internet access (using pip package as an example)

    wget https://files.pythonhosted.org/packages/6a/df/a6ef77a6574781a668791419ffe366c8acd1c3cf4709d210cb53cd5ce1c2/pip-22.0.3-py3-none-any.whl

    Then you push that file to the device you need to install the package on, and you can execute:

    pip install pip-22.0.3-py3-none-any.whl

    Install a list of requirements:

    You first need to download the list of packages needed from the requirements.txt file:

    pip download -r requirements.txt

    Then, push all the downloaded files to the server, and install those requirements on the server, using the downloaded packages:

    pip install --no-index --find-links /path/to/download/dir/ -r requirements.txt
  • Issue with sudo – sudo:auth could not identify password for [user]

    If you get the following error when trying to execute a sudo command, despite the fact that you configured your sudoers or sudoers.d config file properly:

    sudo: pam_unix(sudo:auth): conversation failed
    sudo: pam_unix(sudo:auth): auth could not identify password for [user]

    Then, you might need to have a look at the PAM configuration on your device.
    Ensure that you have this line enabled in your /etc/pam.d/sudo file:

    # cat /etc/pam.d/sudo
    #%PAM-1.0
    # The line below should fix the sudo:auth could not identify password
    auth       sufficient   pam_permit.so
    
    # The lines below are the default config
    auth       include      system-auth
    account    include      system-auth
    password   include      system-auth
    session    optional     pam_keyinit.so revoke
    
    session    include      system-auth
    

    Once you changed this file, no need to restart anything – just retry your command and it should work!

  • Use rsyslog omprog with a Python script

    If you want to filter some specific logs and redirect them to another server or to another file, you can use the module provided with rsyslog called “omprog”.

    Thanks to that module, you can just ask rsyslog to execute a script on every logs you have from a specific application and process it with your own rules.

    Let’s go with a simple example, I want to filter my logs from postfix and only extract few specific information.

    Below is the configuration I will now push to /etc/rsyslog.d/rsyslog_postfix.conf in order to:

    1. Redirect the current log to /var/log/maillog as a default path (to keep the raw log)
    2. Process every log line with my python script in order to extract only some specific informations that I will write in /var/log/rsyslog_postfix.log
    module(load="omprog")
    
    mail.* {
        action(type="omfile" 
            file="/var/log/maillog")
        action(type="omprog"
            name="rsyslog_postfix"
            binary="/usr/bin/python3 /opt/rsyslog_postfix.py"
            queue.type="LinkedList"
            queue.size="20000"
            queue.saveOnShutdown="on"
            queue.workerThreads="4"
            queue.workerThreadMinimumMessages="5000"
            action.resumeInterval="5"
            output="/var/log/rsyslog_postfix.log")
        stop
    }

    Rsyslog script

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    # python_version  :3.6
    #===========================
    
    import sys
    import traceback
    import datetime
    
    if __name__ == '__main__':
        data = {}
        while True:
            try:
                line = sys.stdin.readline()
                msg = line.strip()
                if msg != "":
                    data.clear()
                    timestamp = msg.split(' ')[0]
                    proc= msg.split(' ')[2].split('[')[0]
                    pid = msg.split(' ')[2].split('[')[1].split(']')[0]
                    log = msg.split(' ', 3)[3]
                    if proc.startswith("postfix/"):
                        data = { 
                            "Timestamp": timestamp,
                            "Process": proc,
                            "PID": pid,
                        }
                        data["PostfixID"] = log.split(' ')[0].split(':')[0]
                    print(data)
            except Exception as e:
                err = traceback.format_exc()
                print(err)

    Now, I just need to restart rsyslogd process and the logs will start to be processed as expected, and you should get the raw logs in /var/log/maillog and the filtered ones in /var/log/rsyslog_postfix.log

    systemctl restart rsyslog
  • Disable FIPS mode on CentOS 7

    FIPS (Federal Information Processing Standard) can be enabled (by default or not) on linux kernels to enable the FIPS kernel cryptographic features.
    But in some case, this can also lead to some issues with openssl, or any cryptographic tool that you can use within any code.

    You can check if FIPS is enabled with that command:

    # cat /proc/sys/crypto/fips_enabled
    1

    If you need to turn this feature off, you will have to first remove any dracut-fips package that you have installed:

    # yum -y remove dracut-fips* 

    Then, take a backup of the FIPS initramfs and recreate a new file:

    # cp -p /boot/initramfs-$(uname -r).img /opt/initramfs-$(uname -r).backup
    # dracut -f 

    Once the file creation is complete, update your GRUB configuration to disable fips flag and rebuild grub configuration

    # perl -pi -e 's/fips=1/fips=0/g' /etc/default/grub 
    # grub2-mkconfig -o /boot/grub2/grub.cfg
    # grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg

    Now, reboot the server and if you check FIPS status again, it should return a value of 0 indicating that FIPS is disabled:

    # cat /proc/sys/crypto/fips_enabled
    0
  • Add timestamp on each line of bash output

    When analyzing the output of a bash script, it can be useful to prepend timestamp before each line to see how long it’s taking to do a specific action.

    For this, you can use the ‘ts‘ command:

    # echo -e "this\nis\na\ntest" | ts '[%Y-%m-%d %H:%M:%S]'
    [2019-05-13 09:14:28] this
    [2019-05-13 09:14:28] is
    [2019-05-13 09:14:28] a
    [2019-05-13 09:14:28] test

    If the command ‘ts‘ is not available, you might need to install the package moreutils.

  • Apache/HTTPd Permissions are missing on a component of the path

    Seeing that error in HTTPd/Apache logs when trying to GET some pages?

    (13)Permission denied: [client xxxxx]: access to /html/myfile.html denied because search permissions are missing on a component of the path

    This is because of some SELinux policies blocking the calls. If you don’t want to turn SELinux off, a simple workaround is:

    chcon -R --type=httpd_sys_rw_content_t /html/
  • Fix the NO_PUBKEY error with apt on Ubuntu

    When updating or installing a package on a Debian-based distribution, you can face that alert:

    W: GPG error: http://ppa.launchpad.net trusty Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY C2518248EEA14886

    This just means that a signing GPG key is missing or expired on your system. The error is giving you the ID of the missing key that you can use for looking for it on the Ubuntu website:

    http://keyserver.ubuntu.com:11371/pks/lookup?op=get&search=0xC2518248EEA14886

    Copy the key that you just got from the website above into a file – mykey.txt, for example – and import it with the apt-key command:

    apt-key add mykey.txt

    Try to install/update the package again, and the error should not appear anymore.

  • Extract certificate and/or key from a PKCS12 file

    The PKCS12 format is replacing the old PFX format from Microsoft. This format will allow storage of X.509 private keys and the associated public certificates in a single encrypted file.

    So you can extract the key and the certificate in a single common PEM file, you can use this openssl command:

    openssl pkcs12 -in myCertificate.pfx -out myCertificateAndMyKey.pem
    

    If you want to extract the key and the certificate independently, you can also use the options nocerts/nokeys along with openssl, to extract only one part:

    openssl pkcs12 -in myCertificate.pfx -nocerts -out myCertificate.key
    openssl pkcs12 -in myCertificate.pfx -nokeys -out myCertificate.pem
    
  • Automate apt update and keep current config files

    When doing update on a Debian/Ubuntu server, if a package is trying to modify a config file, it’s asking for a manual choice. If you’re trying to try to automate the process, you’d probably like to get rid of that ask and keep the current config file by default.

    For that, you can use that script:

    #!/bin/bash
    apt-get update
    listUpgrades=`apt list --upgradable |grep upgradable |cut -d/ -f1`
    execUpgrades="DEBIAN_FRONTEND=noninteractive apt-get --yes --assume-yes -o DPkg::options::=\"--force-confdef\" -o DPkg::options::=\"--force-confold\" --only-upgrade install "$listUpgrades
    eval $execUpgrades
    

    The multiple options will enforce the update to keep current config and not overwrite it with the package default one.