17. November 2021 · Comments Off on GnuPG encryption/decryption helper · Categories: Linux, Linux Admin, Linux Scripts, Linux Security · Tags: , , , ,

I have had many ruminations on the best way to encrypt and secure files. A good reason for wanting to encrypt your files might be; you keep a USB stick on your keychain with your taxes or customer data on it so its accesable to you, but you’re are worried about loosing it. In situations like that, file encryption is exactly what you want! I previously have written a scripts and jotted notes on encrypting block devices; of which I am not even convinced is best practice. Encrypting block devices is all well and good but it’s not very practical for encrypting individual files. I recently noticed that Cisco uses GPG to encrypt their backup files on their ISE appliance, which convinced me that GPG is probabbly the smartest way to encrypt files. Unfortunately there is a lot of commandline options and reading you have to do inorder to get it to work properly. I figured this was perfect thing to write a script for and make public. My goal is to make GnuPG a little bit more accesable to a broader audience.

The following script is intended to make it simple for a lay Linux user to use GPG to encrypt/decrypt files and directories. As a bonus I’m keeping the debug block that I used, and can be removed, in order to test encryption and decryption while I was writting the script and testing commands. If you do not already have a public-private key pair, the script will generate one for you and ask you if you want to do a backup. If you allow the script to create backup files of your keys I strongly recommend you remove them as soon as possible! The script can also perform a restore of your keys from backup if you put them back in the root of your home directory. Additionally the script doesn’t see any keys it will perform symmetric encryption against the follow directory. Symmetric encryption just uses a simple passphrase to perform the encryption and decryption. If you create the following variable:

export gpg_method="symmetric"

With the above variable set; the script will ignore your keys and will do symmetric only encryption.

gpg_crypt.sh: a /bin/env bash script, ASCII text executable, with very long lines (382)

#!/bin/env bash
## GnuPG encryption/decryption helper.
## 2021 - Script from www.davideaves.com
# Enable for debuging
# set -x
export GPG_TTY=$(tty)
# Verify script requirements
for req in curl gpg jq
 do type ${req} >/dev/null 2>&1 || { echo >&2 "$(basename "${0}"): I require ${req} but it's not installed. Aborting."; exit 1; }
done && umask 0077
# Fetch existing keys
keyid=( `gpg --list-keys --keyid-format 0xLONG | awk '/^sub.*[E]/{gsub("[]|[]|/", " "); print $3,$NF}'` )
# Help manage keys
if [ -z "${keyid}" ] && [ -z "${gpg_method}" ]
 then [ -f "${HOME}/bin/rc_files/gpg.conf" -a ! -f "${HOME}/.gnupg/gpg.conf" ] && \
       { cat "${HOME}/bin/rc_files/gpg.conf" > "${HOME}/.gnupg/gpg.conf"; }
      # Generate a new keys
      read -p "(G)enerate new or (R)estore keys? (g/r) " -n 1 -r; echo
      if [[ "${REPLY}" =~ ^[Gg]$ ]]
       then gpg --full-generate-key || gpg --gen-key
            # Backup keys
            read -p "Export a backup of keys? " -n 1 -r; echo
            if [[ "${REPLY}" =~ ^[Yy]$ ]]
             then gpg --armor --export-secret-key > "${HOME}/gpg_secret-key.asc"
                  gpg --armor --export-secret-subkeys > "${HOME}/gpg_secret-subkeys.asc"
                  gpg --armor --export > "${HOME}/gpg_public-key.asc"
                  gpg --armor --export-ownertrust > "${HOME}/gpg_ownertrust.txt"
      # Restore existing keys
      elif [[ "${REPLY}" =~ ^[Rr]$ ]]
       then for asc in ${HOME}/gpg_*.asc
             do gpg --import "${asc}"
            done && gpg --import-ownertrust "${HOME}/gpg_ownertrust.txt"
      fi && unset ${REPLY}
 elif [ -n "${gpg_method}" ]
  then unset keyid
# Exit if no debug or user input
if [[ ! "$SHELLOPTS" =~ "xtrace" ]] && [[ -z "${@}" ]]
 then echo "$(basename "${0}"): GnuPG encryption/decryption helper."
      echo "File or Directory input is required to continue!"
 exit 0
# Create debug test file
if [[ "$SHELLOPTS" =~ "xtrace" ]] && [[ -z "${@}" ]]
      # Create temp file
      if debug_file="$(mktemp)"
       then trap "{ if [ -e "${debug_file}*" ]; then rm -rf "${debug_file}*"; fi }" \
       else echo "Failure, exit status: ${?}"
            exit ${?};
      fi && echo -n "${debug_b64}" | base64 -d > "${debug_file}"
      MimeDB=( "https://raw.githubusercontent.com/jshttp/mime-db/master/db.json" "${HOME}/.mime.json" )
      [ ! -s "${MimeDB[1]}" ] && { curl -s "${MimeDB[0]}" | jq > "${MimeDB[1]}"; }
      debug_mime="$(file -b --mime-type "${debug_file}")"
      debug_hash="$(sha1sum "${debug_temp}" | awk '{print $1}')"
      debug_ext="$(jq -r --arg MIME "${debug_mime}" '.[$MIME].extensions[0] // empty' "${MimeDB[1]}")"
      mv "${debug_file}" "${debug_file}.${debug_ext}"
      set -- "$@" "${debug_file}.${debug_ext}"
      file "${debug_file}.${debug_ext}"
### BEGIN ###
for input in "${@}"
 do file_ext="$(basename "${input##*.}")"
    ## symmetric encryption: file ##
    if [ -z "${keyid[0]}" ] && [ -f "${input}" ]
     then if [ "${file_ext}" != "gpg" ]
           then gpg --batch --yes --quiet --output "$(basename "${input:-'null'}").gpg" --symmetric "${input}"
          ## decrypt file ##
          elif [[ "${input%.*}" =~ ".tgz"$ ]]
           then gpg --batch --yes --quiet --decrypt "${input}" | tar xzfv -
           else gpg --batch --yes --quiet --output "$(basename "${input%.*}")" --decrypt "${input}"
    ## symmetric encryption: directory ##
    elif [ -z "${keyid[0]}" ] && [ -d "${input}" ]
     then tar czfv - "${input}" | gpg --batch --yes --quiet --output "$(basename "${input:-'null'}").tgz.gpg" --symmetric
    ## key encryption: file ##
    elif [ -n "${keyid[0]}" ] && [ -f "${input}" ]
     then if [ "${file_ext}" != "gpg" ]
           then gpg --batch --yes --quiet --output "$(basename "${input:-'null'}").gpg" --recipient ${keyid[0]} --encrypt "${input}"
          ## decrypt file ##
          elif [[ "${input%.*}" =~ ".tgz"$ ]]
           then gpg --batch --yes --quiet --recipient ${keyid[0]} --decrypt "${input}" | tar xzfv -
           else gpg --batch --yes --quiet --output "$(basename "${input%.*}")" --recipient ${keyid[0]} --decrypt "${input}"
    ## key encryption: directory ##
    elif [ -n "${keyid[0]}" ] && [ -d "${input}" ]
     then tar czfv - "${input}" | gpg --batch --yes --quiet --recipient ${keyid[0]} --encrypt > "$(basename "${input:-'null'}").tgz.gpg"
### FINISH ###

GnuPG user configuration options

If you use GnuPG I recommend updating your configuration to give it an affinity for stronger ciphers. Riseup.net created a very good best practice config that is a good starting place. I’ve made a few modifications to it, but have left it relatively unchanged. If the above script sees the user config missing and can find the following config file in a repo direcory, it will go ahead and copy the configuration to where it needs to be… Feel free to use the below config as an optional reference.

gpg.conf: ASCII text

## GnuPG Options
# Assume that command line arguments are given as UTF8 strings.
# This is an implementation of the Riseup OpenPGP Best Practices
# https://help.riseup.net/en/security/message-security/openpgp/best-practices
# default key
# The default key to sign with. If this option is not used, the default key is the first key found in the secret keyring
#default-key 0xD8692123C4065DEA5E0F3AB5249B39D24F25E3B6
# behavior
# Disable inclusion of the version string in ASCII armored output
# Disable comment string in clear text signatures and ASCII armored messages
# Display long key IDs
keyid-format 0xlong
# List all keys (or the specified ones) along with their fingerprints
# Display the calculated validity of user IDs during key listings
list-options show-uid-validity
verify-options show-uid-validity
# Try to use the GnuPG-Agent. With this option, GnuPG first tries to connect to the agent before it asks for a passphrase.
# algorithm and ciphers
# list of personal digest preferences. When multiple digests are supported by all recipients, choose the strongest one
personal-cipher-preferences AES256 AES192 AES CAST5
# list of personal digest preferences. When multiple ciphers are supported by all recipients, choose the strongest one
personal-digest-preferences SHA512 SHA384 SHA256 SHA224
# message digest algorithm used when signing a key
cert-digest-algo SHA512
# This preference list is used for new keys and becomes the default for "setpref" in the edit menu
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
# Use a specified algorithm as the symmetric cipher
cipher-algo AES256
27. November 2017 · Comments Off on Preventing brute force login attempts by merging geoiplookup and hosts_access · Categories: Ansible, Linux, Linux Admin, Linux Scripts, Linux Security, Networking · Tags: , , , , , ,

To me networking and UNIX run hand-in-hand; as I have been getting into Ansible to do network automation I am also using it to automate and enforce consistency between all of my other hosts. The following is a simple playbook to configure Debian based hosts to perform a geoiplookup against all incoming hosts to prevent brute force login attempts using host_access. I have to admit the original script and idea are not mine; they originated from the following blog post: Limit your SSH logins using GeoIP. As more and more systems are migrated into cloud environments automating and enforcing security controls like this one are of critical importance.

geowrapper.yaml: a /usr/local/bin/ansible-playbook script, ASCII text executable

## Configure Debian OS family to geoiplookup against all incoming hosts.
## 2017 (v.01) - Playbook from www.davideaves.com
- name: GEO Wrapper
  hosts: all
  become: yes
  gather_facts: yes
  tags: host_access

    geocountries: "US"
    geofilter: "/opt/geowrapper.sh"

  - name: "Fail if OS family not Debian"
      msg: "Distribution not supported"
    when: ansible_os_family != "Debian"

  - name: "Fetch geoip packages"
        - geoip-bin
        - geoip-database
      state: latest
      update_cache: yes
    register: geoip
    when: ansible_os_family == "Debian"

  - name: "Wrapper script {{ geofilter }}"
      content: |
        # Ansible Managed: GeoIP aclexec script for Linux TCP wrappers.
        ## Source: http://www.axllent.org/docs/view/ssh-geoip
        # UPPERCASE space-separated country codes to ACCEPT
        ALLOW_COUNTRIES="{{ geocountries }}"
        if [ $# -ne 1 ]; then
          echo "Usage:  `basename $0` ip" 1>&2
          exit 0 # return true in case of config issue
        COUNTRY=`/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1`
        [[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY"
        if [ $RESPONSE = "ALLOW" ]
          exit 0
          logger "$RESPONSE connection from $1 ($COUNTRY)"
          exit 1
      dest: "{{ geofilter }}"
      mode: "0755"
      owner: root
      group: root
    ignore_errors: yes
    register: geowrapper
    when: geoip|success

  - name: "Mappings in /etc/hosts.allow"
      path: /etc/hosts.allow
      state: present
      content: |
        ALL: ALL: aclexec {{ geofilter }} %a
    when: geowrapper|success

  - name: "Mappings in /etc/hosts.deny"
      path: /etc/hosts.deny
      state: present
      content: "ALL: ALL"
    when: geowrapper|success
25. February 2015 · Comments Off on Root Auth Monitor: iptables DROP evil networks · Categories: Linux, Linux Admin, Linux Scripts, Linux Security, Networking · Tags: , , , , ,

The following is an upstart script that monitors & blocks networks that fail to log into your Ubuntu server as root. Its great script to stop brute force logins to your server.

The following are a couple commands for reference:

Start/Stop the script…
start tty12
stop tty12

List INPUT rules w/line numbers…
iptables -L INPUT -n –line-numbers

Delete an INPUT rule by line number…
iptables -D INPUT 1

# /etc/init/tty12 - Root Auth Monitor: iptables DROP evil networks
# Required modifying/adding PermitRootLogin & AllowUsers to /etc/ssh/sshd_config
start on runlevel [23] and not-container
stop on runlevel [!23]
exec > /dev/tty12
tail -fn0 /var/log/auth.log | while read LINE
do echo "$LINE" | grep ": Failed password for invalid user root from"
 if [ $? = 0 ]
   whois -h whois.cymru.com " -p $(echo "$LINE" | awk '{print $13}')" | grep ^[0-9] | sed 's/ *| */|/g' |\
   while IFS="|" read AS IP PREFIX NAME
    do iptables -I INPUT -s $PREFIX -j DROP -m comment --comment "AS$AS: $NAME"
end script
15. November 2014 · Comments Off on Dynamic Cisco Port Knocking EEM applet · Categories: Cisco, EEM, Linux Security, Networking · Tags: , ,

There is nothing new about port knocking to hide remote access to a remote system or network. However its usually implemented as a hack thats done on a single host thats sitting at a remote site. If your running a Cisco router the only method to get port knocking working is to create an EEM applet. I have seen several port knocking EEM applets online, but none of them seem very good and they usually work by swapping out a less secure ACL for a more secure one.

After watching my server logs  get obliterated by some knuckle head trying to brute force their way into it, I decided to get port knocking working on a Cisco router. The following EEM applet uses an extended ACL thats tied to the inbound WAN interface of the router. The ACL has a permit statement that will log a certain packet type; the logs will be updated with a notification that contains the originating IP address. The EEM applet monitors the logs and will be triggered when it sees the ACL. The applet will then pull out the knocking IP address and temporarily add it to the same inbound ACL allowing enough time to establish a connection from the originating machine. After 15 seconds have passed, the script will drop the permit line that was added to the ACL.

ip access-LIST extended outside-in4
 remark *** KNOCK ***
 permit udp ANY ANY eq 65535 LOG
 remark *** TRUSTED ***
 permit tcp ANY ANY established
 remark *** DENIED ***
 deny   tcp ANY ANY
 remark *** PERMITED ***
 permit ip ANY ANY
!! WAN Interface !!
interface Cable-Modem0/1/0
 ip access-group outside-in4 in
!! KNOCK_ACL env Variable !!
event manager environment KNOCK_ACL outside-in4
!! Port Knocking EEM applet !!
event manager applet KNOCK
 event syslog pattern "%SEC-6-IPACCESSLOGP: list $KNOCK_ACL permitted *"
 action 1.0 regexp "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" $_syslog_msg ADDR
 action 1.1 regexp "\([0-9]+\)," "$_syslog_msg" PORT
 action 1.2 regexp "[0-9]+" "$PORT" PORT 
 action 2.0 syslog msg "Received a knock from $ADDR on port $PORT..."
 action 2.1 syslog msg "Adding $ADDR to the $KNOCK_ACL ACL"
 action 3.0 cli command "enable"
 action 3.1 cli command "configure terminal"
 action 3.2 cli command "ip access-list extended $KNOCK_ACL"
 action 3.3 cli command "1 permit tcp host $ADDR any eq 22"
 action 4.0 WAIT 15
 action 5.0 syslog msg "Removing $ADDR to the $KNOCK_ACL ACL"
 action 6.0 cli command "no permit tcp host $ADDR any eq 22"
 action 6.1 cli command "exit"

The only tricky thing in the above example is creating a good regex expression… Knocking with a UDP/65535 packet and immediately connecting to the routers WAN IP address will allow you to SSH into a server on the other side. The following is me crafting a simple UDP packet using hping3 under Linux.

$ sudo hping3 -2 ROUTERIP -p 65535 -c 1; ssh ROUTERIP

A few caveats:

  • Usually any/any ACL’s are not good, but in my case, this is a home router doing PAT and a DHCP client on the WAN interface.
  • Show active EEM policies: show event manager policy active
  • Show EEM history: show event manager history events
  • Validate the ACL is getting hit: show access-list outside-in4
  • The default EEM watchdog will terminate the applet after 20 seconds. MAXRUN will need to be changed if you want the applet to wait longer then 15 seconds before auto terminating.
  • The ACL can be modified to log packet options such as special ToS, DSCP values in addition to ports.
  • I recommend not using other log statements in the same ACL, doing so will require making a more custom applet.
  • If your trying to log into the router itself via an ACL on a TTY line be mindful of any service-polices you have bound to the control-plane.
18. August 2014 · Comments Off on LUKS Quick Examples · Categories: Linux Admin, Linux Security · Tags: , ,

Required Debian/Ubuntu Packages

dmsetup Linux Kernel Device Mapper userspace library
cryptsetup-bin Disk encryption support – command line tools
tcplay Free and simple TrueCrypt Implementation based on dm-crypt

Filesystem Encryption

cryptsetup –cipher aes-xts-plain64 –key-size 512 –verify-passphrase luksFormat /dev/sdb1
* The LUKS-formatting command above has the following options:

–verify-passphrase – ensures the passphrase is entered twice to avoid an incorrect passphrase being used
-c aes -s 256 – uses 256-bit AES encryption
-h sha256 – uses the 256-bit SHA hashing algorithm

Creating a Filesystem

cryptsetup luksOpen /dev/sdb1 16GB
mkfs -t ext3 -m 1 -O dir_index,filetype,sparse_super /dev/mapper/16GB

* The mkfs options above are as follows:

-t ext3 – create an ext3 filesystem
-m 1 – reduce the reserved super-user space down from the default of 5% to 1% of the total size – useful for large filesystems
-O dir_index – speed-up lookups in large directories
-O filetype – store filetype info in directories
-O sparse_super – create fewer superblock backup copies – useful for large filesystems

Mounting a Filesystem

cryptsetup luksOpen /dev/sdb1 16GB
mount /dev/mapper/16GB /mnt
* To mount a truecrypt partition:

tcplay -m 16GB -d /dev/sdc1
dmsetup remove 16GB

Change Passwords on a Filesystem

LUKS supports eight key slots per partition.
To add and remove keys from the slots:

cryptsetup luksAddKey
cryptsetup luksRemoveKey

Which slots have keys:

cryptsetup luksDump

Headers on a Filesystem

cryptsetup luksHeaderBackup /dev/sdb1 –header-backup-file /tmp/somefile
Replace luksHeaderBackup with luksHeaderRestore to restore the old keys again.

* Note that the header backup should be saved to a secure place (preferably another LUKS partition on a USB stick)

Unmount a Filesystem

Use umount first then,
cryptsetup luksClose 16GB
dmsetup remove 16GB
dmsetup remove_all

* dmsetup remove_all will flush all mapped block devices.

Source & Additional Documentation