Category Archives: Linux

2FA for SSH/GDM

Please emerge qrencode and google-authenticator-libpam-hardened.
At the time of writing this, you have to install the MASKED version.

Ensure that your terminal is 90x56 to retain the QR code.

ACCEPT_KEYWORDS="**" emerge -av google-authenticator-libpam-hardened

These are the packages that would be merged, in order:

Calculating dependencies… done!
[ebuild N ] sys-auth/oath-toolkit-2.6.2-r2::gentoo USE="pam -pskc -static-libs -test" 4,196 KiB
[ebuild N ] sys-auth/google-authenticator-libpam-hardened-9999::gentoo USE="qrcode" 0 KiB

Execute google-authenticator as the user

$ google-authenticator

Do you want authentication tokens to be time-based (y/n) y



Your new secret key is: jeronimo!
Your potential verification codes are 012345 012345 012345 012345 012345
Your emergency scratch codes are:
  88888888
  88888888
  88888888
  88888888
  88888888

Do you want me to update your "/home/<user>/.google_authenticator" file? (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) y

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y

Open /etc/pam.d/system-login with your editor and add these 2 lines at the bottom:

auth required pam_google_authenticator.so nullok
auth required pam_permit.so

Configure SSHD

Open /etc/sshd/sshd_config in your editor and ensure the following settings are present.

PubkeyAuthentication no
PasswordAuthentication yes
UsePAM yes
ChallengeResponseAuthentication yes

Note: If pubkey is enabled, it will override 2FA completely.

GDM

Editing /etc/pam.d/system-login as above should be enough, just restart the gdm service :)
You'll use your normal password and then be prompted for your 2FA code.

QR Codes

You can generate your own QR codes on the cli and output to screen or image file.

qrencode -t ansi -l H -o - <STRING>

will output to screen.

 qrencode -s30 -l H -o qrcode.png <STRING>

will output to png.

SPF, DKIM, DMARC, MTA-STS

Securing and reducing spam is an ongoing battle.

Prerequisits:
* DNSSEC [usually managed by your domain provider and if you run bind]
* PTR [usually setup by your ISP unless you run an authoritative DNS. Implies a static IP]
* Exim >4.7

Useful Links:
http://www.dnssec-or-not.com
https://dnssec-analyzer.verisignlabs.com
https://dnschecker.org/domain-health-checker.php
https://en.internet.nl/

SPF (Sender Policy Framework)

You can put the following into it's own config eg acl_check_spf or place it in the global acl (acl_check_data:).

    deny condition = ${if eq{$sender_helo_name}{} {1}}
         message = Nice bots say HELO first

    # reject messages from senders listed in these DNSBLs
    deny dnslists = zen.spamhaus.org

    # SPF validation
    deny spf = fail : softfail
            message = SPF validation failed: \
                    $sender_host_address is not allowed to send mail from \
                    ${if def:sender_address_domain \
                        {$sender_address_domain}{$sender_helo_name}}
            log_message = SPF validation failed\
                    ${if eq{$spf_result}{softfail} { (softfail)}{}}: \
                    $sender_host_address is not allowed to send mail from \
                    ${if def:sender_address_domain \
                        {$sender_address_domain}{$sender_helo_name}}
    deny spf = permerror
            message = SPF validation failed: \
                    syntax error in SPF record(s) for \
                    ${if def:sender_address_domain \
                        {$sender_address_domain}{$sender_helo_name}}
            log_message = SPF validation failed (permerror): \
                    syntax error in SPF record(s) for \
                    ${if def:sender_address_domain \
                        {$sender_address_domain}{$sender_helo_name}}
    defer spf = temperror
            message = temporary error during SPF validation; \
                    please try again later
            log_message = SPF validation failed temporary; deferred
    # Log SPF none/neutral result
    warn spf = none : neutral
            log_message = SPF validation none/neutral

    # Use the lack of reverse DNS to trigger greylisting. Some people
    # even reject for it but that would be a little excessive.

    warn condition = ${if eq{$sender_host_name}{} {1}}
         set acl_m_greylistreasons = Host $sender_host_address \
             lacks reverse DNS\n$acl_m_greylistreasons

    accept
            # Add an SPF-Received header to the message
            add_header = :at_start: $spf_received
            logwrite = SPF validation passed

You will also need a TXT record publishing with the registrar and/or internal DNS.

Host nameTypeTTLData
example.comTXT1 hour"v=spf1 ip4:xxx.xxx.xxx.xxx ip6::1 -all"

Looking at the record itself, we see that the version indicator, 'v=spf1', is followed by a typical SPF policy: first a list of systems that are authorised to send mail for the domain, then '-all', which means that all other systems are not authorised. The alternative to ending the record with '-all' is to end with '~all'. That is known as a 'soft fail', meaning that messages from non-validating systems should not be blocked, but forwarded with a tag.

DKIM (Domain Keys Identified Mail)

Before the ACL Configuration, place the following:

#  # DKIM macros
#  # get the sender domain from the outgoing mail
  SENDER_DOMAIN = ${if def:h_from:{${lc:${domain:${address:$h_from:}}}}{$qualify_domain}}
#  # the key file name will be based on the domain name in the From header
  DKIM_KEY_PATH = /etc/exim/keys
  DKIM_KEY_FILE = dkim_rsa.private

Put the following under the ACL Configuration.

# This access control list is used to process DKIM status.
acl_check_dkim:

  # Skip DKIM checks for all authenticated connections (probably MUAs)
  accept
          authenticated = *

  # Record the current timestamp, in order to delay crappy senders
  warn
          set acl_m0  = $tod_epoch

  # Warn no DKIM
  warn
          dkim_status = none
          set acl_c4  = X-DKIM-Warning: No signature found

  # RFC 8301 requires 'permanently failed evaluation' for DKIM signatures signed with 'historic algorithms (currently, rsa-sha1)'
  # @SEE: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-dkim_and_spf.html
  warn
          condition              = ${if !def:acl_c4 {true}{false} }
          condition              = ${if eq {$dkim_verify_status}{pass} }
          condition              = ${if eq {${length_3:$dkim_algo} }{rsa} }
          condition              = ${if or { {eq {$dkim_algo}{rsa-sha1} } \
                                    {< {$dkim_key_length}{1024} } } }
          set acl_c4             = X-DKIM-Warning: forced DKIM failure (weak hash or short key)
          set dkim_verify_status = fail
          set dkim_verify_reason = hash too weak or key too short

  # RFC6376 requires that verification fail if the From: header is not included in the signature
  # @SEE: https://www.exim.org/exim-html-current/doc/html/spec_html/ch-dkim_and_spf.html
  warn
          condition   = ${if !def:acl_c4 {true}{false} }
          condition   = ${if !inlisti{from}{$dkim_headernames}{true}{false} }
          set acl_c4  = X-DKIM-Warning: From: header not included in the \
                        signature, this defies the purpose of DKIM

  # Warn invalid or failed signatures
  warn
          condition   = ${if !def:acl_c4 {true}{false} }
          dkim_status = fail:invalid
          set acl_c4  = X-DKIM-Warning: verifying signature of $dkim_cur_signer \
                        failed for $sender_address because $dkim_verify_reason

  # Add a DKIM-Received: line to the message header (regardless of DKIM status)
  warn
          add_header  = Received-DKIM: $dkim_verify_status ${if \
                        def:dkim_cur_signer {($dkim_cur_signer with \
                        $dkim_algo for $dkim_headernames)} }

  # Set up for finalisation: add header and write to log
  warn
          condition   = ${if def:acl_c4 {true}{false} }
          add_header  = $acl_c4
          logwrite    = $acl_c4

accept

Again a TXT record needs to be defined.

Host nameTypeTTLData
<selector>._domainkey.example.comTXT1 hour"v=DKIM1; k=rsa; p="encrypted rsa key"

To enable DKIM-validating mail servers to validate our digital signatures, the public key from the DKIM key pair generated earlier has to be published in the zone file of the signing domain. The first step is to generate the public key from the DKIM key file:

  [root@system keys]# openssl rsa -in dkim_rsa.private -out /dev/stdout -pubout -outform PEM
  writing RSA key
  -----BEGIN PUBLIC KEY-----
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxMUk9Ac+aZVcqPkgSPny
  UOkWGrIvXcMJvUHjObpWlMNix3D74hE4KZ+Z18ZvOCUlUQGftzv0MJND/S4kXMlJ
  xuoxNMCKGozD/O71Rblz7RDUHxrhud2rjtSmXdmDHpH713djNiIxxZgeEeNBzfX3
  UGdCJlRMVQJXUcEozqgI5BmUTsdYtrb2Trr99IZtgaLEI92yXVdholtIyt83gnhA
  YLnvAzOQRV4zE/eBB/pfpbFrkPh1uQQxVIBi0pARj3xk9B8yXiCXUX+gyyBrw3zi
  /rnXFDe0ORjtDo/3WsSrwaivJ6KjywauYgnwYAx1eNyBGnPquVR6d8OlI15YIXy+
  1wIDAQAB
  -----END PUBLIC KEY-----

The public key can be inserted directly into a DKIM record as follows:

  dkim202205615._domainkey.example.com.    3600 TXT (
    "v=DKIM1; p="
    "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxMUk9Ac+aZVcqPkgSPny"
    "UOkWGrIvXcMJvUHjObpWlMNix3D74hE4KZ+Z18ZvOCUlUQGftzv0MJND/S4kXMlJ"
    "xuoxNMCKGozD/O71Rblz7RDUHxrhud2rjtSmXdmDHpH713djNiIxxZgeEeNBzfX3"
    "UGdCJlRMVQJXUcEozqgI5BmUTsdYtrb2Trr99IZtgaLEI92yXVdholtIyt83gnhA"
    "YLnvAzOQRV4zE/eBB/pfpbFrkPh1uQQxVIBi0pARj3xk9B8yXiCXUX+gyyBrw3zi"
    "/rnXFDe0ORjtDo/3WsSrwaivJ6KjywauYgnwYAx1eNyBGnPquVR6d8OlI15YIXy+"
    "1wIDAQAB")

Note the 'dkim20220615': that is the 'selector', which specifies the key pair used for signing. As you'll see shortly, the selector is also included in the 'DKIM Signature' header, so that when the receiving mail server follows the validation procedure, it knows exactly which public key to request from the DNS.

DMARC (Domain-based Message Authentication, Reporting and Conformance)

Put the following before the ACL Configuration.

# DMARC
  dmarc_tld_file=/usr/share/publicsuffix/public_suffix_list.dat
  dmarc_history_file=/var/spool/exim/opendmarc/history.dat
  dmarc_forensic_sender=postmaster@example.com

Put the following under the ACL Configuration.

acl_check_data:
# DMARC
  warn    dmarc_status = quarantine
          !authenticated = *
          log_message = Message from $dmarc_used_domain failed sender's DMARC policy; quarantine
          #control = dmarc_enable_forensic
          set acl_m_quarantine = 1
          # this variable to use in a router/transport
  deny    dmarc_status = reject
          !authenticated = *
          message = Message from $dmarc_used_domain failed sender's DMARC policy; reject
          #control = dmarc_enable_forensic
  warn    add_header = :at_start: ${authresults {$primary_hostname}}

You'll also need to generate the key pair.
The DKIM key pair is generated as follows:

  mkdir /etc/exim/keys/
  cd /etc/exim/keys/
  openssl genrsa -out dkim_rsa.private 2048

The new file 'dkim_rsa.private' contains the private key, which has to be kept secret. It's therefore important to ensure that the key file access rights provide appropriate security:

  chmod 640 dkim_rsa.private
  chown root:exim dkim_rsa.private

Although generating a longer key (4096 bits, rather than 2048 bits) is an option, DKIM signatures remain valid for relatively short periods. They are, after all, used exclusively for delivering messages, which, even in the worst-case scenario, only takes a few days. Restricting the key length to 2048 bits allows DNS traffic to go via the efficient UDP protocol, whereas it would be necessary to switch to the more onerous TCP protocol if longer keys were used.

As usual, you will need to submit a DMARC record to DNS:

Host nameTypeTTLData
_dmarc.example.comTXT6 hours"v=DMARC1;p=reject;rua=mailto:example@example.com;ruf=mailto:example@example.com;fo=1;aspf=r;adkim=r;"

I also added the following to the Transports section of exim.conf.

quarantine_delivery:
  driver = appendfile
  directory = /home/$local_part_data/Maildir/.INBOX.quarantine
  maildir_format
  delivery_date_add
  envelope_to_add
  return_path_add

Make sure the directory exists on the mail server.

I also have a cron setup to download the dat file (referenced above):

# DMARC
03 5 * * 1 cd /usr/share/publicsuffix && wget -c https://publicsuffix.org/list/public_suffix_list.dat

MTA-STS (Mail Transfer Agent Strict Transport Security)

This is just adding 2 TXT entries into DNS.

Host nameTypeTTLData
_mta-sts.exmaple.comTXT1 hour"v=STSv1; id=0002"
_smtp._tls.example.comTXT1 hour"v=TLSRPTv1;rua=mailto:example@example.com"

More info can on this can be found here (yes it's the UK gov)

NVME Stats

Checking NVMe Flash Health

If you move to newer generation NVMe-based flash storage, smartctl won’t work anymore. It looks like support for NVMe in Smartmontools is coming, and it would be great to get a single tool that supports both  SATA and NVMe flash storage.

In the meantime, you can use the nvme tool available from the nvme-cli package. It provides some basic information for NVMe devices.

To get information about the NVMe devices installed:

#nvme list
Node SN Model Namespace Usage Format FW Rev ---------------- -------------------- ---------------------------------------- --------- -------------------------- ---------------- -------- /dev/nvme0n1 S4EWNG0M116212J Samsung SSD 970 EVO Plus 1TB 1 713.05 GB / 1.00 TB 512 B + 0 B 1B2QEXM7

To get SMART information:

#nvme smart-log /dev/nvme0
Smart Log for NVME device:nvme0 namespace-id:ffffffff
critical_warning : 0
temperature : 37 C
available_spare : 100%
available_spare_threshold : 10%
percentage_used : 0%
data_units_read : 13,820,831
data_units_written : 20,647,263
host_read_commands : 197,831,770
host_write_commands : 499,344,371
controller_busy_time : 1,663
power_cycles : 36
power_on_hours : 8,091
unsafe_shutdowns : 18
media_errors : 0
num_err_log_entries : 0
Warning Temperature Time : 0
Critical Composite Temperature Time : 0
Temperature Sensor 1 : 37 C
Temperature Sensor 2 : 34 C
Thermal Management T1 Trans Count : 0
Thermal Management T2 Trans Count : 0
Thermal Management T1 Total Time : 0
Thermal Management T2 Total Time : 0

Available Spare. Contains a normalized percentage (0 to 100%) of the remaining spare capacity that is available.

Available Spare Threshold. When the Available Spare capacity falls below the threshold indicated in this field, an asynchronous event completion can occur. The value is indicated as a normalized percentage (0 to 100%).

Percentage Used. Contains a vendor specific estimate of the percentage of the NVM subsystem life used, based on actual usage and the manufacturer’s prediction of NVM life.

(Note: the number can be more than 100% if you’re using storage for longer than its planned life.)

Data Units Read/Data Units Written. This is the number of 512-byte data units that are read/written, but it is measured in an unusual way. The first value corresponds to 1000 of the 512-byte units. So you can multiply this value by 512000 to get value in bytes. It does not include meta-data accesses.

Host Read/Write Commands. The number of commands of the appropriate type issued. Using this value, as well as one below, you can compute the average IO size for “physical” reads and writes.

Controller Busy Time. Time in minutes that the controller was busy servicing commands. This can be used to gauge long-term storage load trends.

Unsafe Shutdowns. The number of times a power loss happened without a shutdown notification being sent. Depending on the NVMe device you’re using, an unsafe shutdown might corrupt user data.

Warning Temperature Time/Critical Temperature Time. The time in minutes a device operated above a warning or critical temperature. It should be zeroes.

Wear_Leveling. This shows how much of the rated cell life was used, as well as the min/max/avg write count for different cells. In this case, it looks like the cells are rated for 1800 writes and about 1100 on average were used

Timed Workload Media Wear. The media wear by the current “workload.” This device allows you to measure some statistics from the time you reset them (called the “workload”) in addition to showing the device lifetime values.

Timed Workload Host Reads. The percentage of IO operations that were reads (since the workload timer was reset).

Thermal Throttle Status. This shows if the device is throttled due to overheating, and when there were throttling events in the past.

Nand Bytes Written. The bytes written to NAND cells. For this device, the measured unit seems to be in 32MB values. It might be different for other devices.

Host Bytes Written. The bytes written to the NVMe storage from the system. This unit also is in 32MB values. The scale of these values is not very important, as they are the most helpful for finding the write amplification of your workload. This ratio is measured in writes to NAND and writes to HOST. For this example, the Write Amplification Factor (WAF) is 16185227 / 6405605 = 2.53  

Regex

Only extract valid IPv4 addresses.

echo '01-01-1970 - Some text about yo mama 255.168.001.255 - fo realz' | grep -P "(?:\b(?:\.)?[2][0-5][0-5](?:\.)?\b|\b(?:\.)?[0-1]\d\d(?:\.)?\b|\b(?:\.)?\b\d\d\b(?:\.)?\b|\b(?:\.)?\b\d\b(?:\.)?\b){4}"

Gnome File Indexing

Tracker is a file indexing and search tool for Linux. Gnome makes use of it for some of its functionality, and as a result, Tracker is installed by default.

The tool speeds up searching and enables full-text search in the Files app, makes the metadata-based batch rename feature to work in the Files app, and enables file and folder search in the Activities Overview. There are some GNOME apps that depend on it too (and don't work at all without it), like Music or Photos. Without Tracker, you'll lose these features, so take this into consideration before completely disabling Tracker.

While it brings a number of useful features to the GNOME desktop, Tracker can also have a performance impact in some cases. These performance issues are supposedly fixed. But there are still users encountering performance issues with Tracker, or users who consider it too resource intensive.

The official way of disabling Tracker on Gnome desktops is to go to Settings -> Search, and turn off the switch from the search settings headerbars (top of the window). There are users however, claiming that this does not disable it, so I decided to try it out, and after turning this option off and a system reboot, tracker status claimed it has more than 100000 files in its index, and it's currently indexing files. But you can give this a try if you wish, and see if it has any impact on your system.

So how to completely disable Tracker, so it no longer indexes any files, and stop having any Tracker process running in the background? You can mask the Tracker systemd services to completely disable it for your current user using this command:

systemctl --user mask tracker-store.service tracker-miner-fs.service tracker-miner-rss.service tracker-extract.service tracker-miner-apps.service tracker-writeback.service

After this, reset Tracker:

tracker reset --hard

CAUTION: This process may irreversibly delete data.
Although most content indexed by Tracker can be safely reindexed, it can’t be assured that this is the case for all data. Be aware that you may be incurring in a data loss situation, proceed at your own risk.

Are you sure you want to proceed? [y|N]: y
Found 3 PIDs…
Killed process 1357 — “tracker-miner-fs”
Killed process 2614 — “tracker-extract”
Killed process 42269 — “tracker-store”
_g_io_module_get_default: Found default implementation dconf (DConfSettingsBackend) for ‘gsettings-backend’
Setting database locations
Checking database directories exist
Checking database version
Checking whether database files exist
Removing all database/storage files
Removing database:'/home/cdstealer/.cache/tracker/meta.db'
Removing db-locale file:'/home/cdstealer/.cache/tracker/db-locale.txt'
Removing journal:'/home/cdstealer/.local/share/tracker/data/tracker-store.journal'
Removing db-version file:'/home/cdstealer/.cache/tracker/db-version.txt'

You could opt to uninstall the tracker completely, but it could be problematic. So instead, I disabled as above as I'm the only user. It never caused any real issues, but I don't need that level of indexing as I almost never use gnome search.

My desktop has 32Gb of RAM and was always full of cache generated by Tracker. So after disabling Tracker, I ran the following command (as root) to clear the cache in RAM.

sync; echo 1 > /proc/sys/vm/drop_caches

DNS Firewall

If you're running your own DNS server as described here, then you can easily setup your domain zone to block ads, malware, phishing etc etc.
I'll describe the process here.

In named.conf, add the following within the options block:

response-policy {
  zone "sinkhole";
};

Next is to download the RPZ (Response Policy Zone) file from a reputable source. For the purpose of this, I'll be using EnergizedProtection.
This is ~25Mb in size and contains over 900,000 entries.
Next I added a new zone to named.conf:

zone "sinkhole" IN {
        type master;
        file "pri/sinkhole.zone";
        notify yes;
        allow-update { key "rndc-key"; };
};

Though I discovered that a few lines were too long. So before restarting named, run a check:

named-checkconf

will check named.conf for errors and

named-checkzone sinkzone /var/bind/pri/sinkhole.zone

will check the zone file for issues.

# named-checkzone sinkzone /var/bind/pri/sinkhole.zone
dns_master_load: /var/bind/pri/sinkhole.zone:316077: ran out of space
dns_master_load: /var/bind/pri/sinkhole.zone:467504: ran out of space
zone sinkzone/IN: loading from master file /var/bind/pri/test failed: ran out of space
zone sinkzone/IN: not loaded due to errors.

This means that the given line numbers in the zone file are too long.

316077 https.outlook.live.com.user0500.deor.error.c8nkichfistk8dphfvkfd9ssli82.is38avdj8h0k381gx0id7hhkg8l.6dls9sz6hv72290ddkuhs.7lxhhjh86k0f2hrivsb1jku718.7lxhhjh86k0f2hrivsb1jku718.h7g6fi9d0fhy6kk6htk4.kwddz0mtsqe28sh3wkj9nhhsd6drh.linestarts.duckdns.org CNAME .
467504 paypal.com.us.continue.myaccount.account.active.login.us.intl.internationa.transfer.now.login.myaccount.account.active.login.us.intl.internationa.transfer.now.myaccount.account.active.login.us.intl.internationa.transfer.now.newmanhope.duckdns.org CNAME .

To workaround this I've written an update script which is run by cron on a weekly basis.

#!/usr/bin/env bash

zfile='/var/bind/pri/sinkhole.zone'
wget -O- https://block.energized.pro/ultimate/formats/rpz.txt | grep -Pv "(\.)?care2\.com|^#" > "${zfile}"
errs=($(/usr/sbin/named-checkzone sinkzone "${zfile}" | pcregrep -o1 ':(\d+):'))
x=0
for a in ${errs[@]}; do
  a=$[a - ${x} ]
  sed -i "${a}d" "${zfile}"
  x=$[x + 1 ]
done
errs=($(/usr/sbin/named-checkzone sinkzone "${zfile}" | pcregrep -o1 ':(\d+):'))
if [ -z ${errs} ]; then
  echo "Success, restarting named"
  systemctl restart named
else
  echo -e "We missed some errors in ${zfile}.  On lines:\n${errs}"
fi

That's it...done!

Changes this:

into this:

SSH Agent

Adding your SSH key to the ssh-agent

Before adding a new SSH key to the ssh-agent to manage your keys, you should have checked for existing SSH keys and generated a new SSH key.
Doing this weakens the security, but only if someone has access to your account or your system has already been compromised.
SSH-AGENT is not a daemon and must be started upon each login which will ask for the passphrase. You *could* NOHUP the command so it stays active between reboots. But I'd avoid doing this.

Start the ssh-agent in the background.

$ eval "$(ssh-agent -s)"
> Agent pid 59566

Add your SSH private key to the ssh-agent. If you created your key with a different name, or if you are adding an existing key that has a different name, replace id_rsa in the command with the name of your private key file.

$ ssh-add ~/.ssh/id_rsa

Splunk tweaks

Set the default search time period.
$SPLUNK_HOME/etc/system/local/ui-prefs.conf file includes:

[search]
 dispatch.earliest_time = -15m
 dispatch.latest_time = now

----------------------

Handy Exim Filters

For a while now I'd been thinking about how to block spam that is mascarading as myself. Today, I fixed it :)
In my exim.conf, I have a section for spam filtering under the "acl_check_data" section.

warn  message = X-Antivirus-Scanned: Clean
warn  message = X-Spam-Score: $spam_score
      spam = mail:true
      condition = ${if <{$message_size}{80k}{true}{false}}
warn  message = X-SA-Status: No
      spam = mail:true
      condition = ${if <{$spam_score_int}{29}{true}{false}}
warn  message = X-Spam-Report: $spam_report
      spam = mail:true
      condition = ${if >{$spam_score_int}{40}{true}{false}}
warn  message = Subject: [SPAM] $h_Subject
      spam = mail:true
      condition = ${if >{$spam_score_int}{30}{true}{false}
warn  message = X-SA-Status: Yes
      spam = mail:true
      condition = ${if >{$spam_score_int}{30}{true}{false}}
warn message = X-Spam-Score: 80
spam = mail:true
condition = ${if !match{$sender_host_address:}{"(127.0.0.1|198.16.181.0/24)"}{true}{false}}}}
condition = ${if match{$sender_address:}{"(me|other.me)"@}{true}{false}}
condition = ${if match{$h_to:}{"(me|other.me)"@}{true}{false}}
deny message = This message scored $spam_score spam points. spam = mail:true condition = ${if >{$spam_score_int}{44}{true}{false}}

I also added the following under the acl_check_helo:

# If the HELO pretend to be this host
deny    condition = ${if or { \
                     {eq {${lc:$sender_helo_name}}{cdstealer.com}} \
                     {eq {${lc:$sender_helo_name}}{127.0.0.1}} \
                     {eq {${lc:$sender_helo_name}}{198.16.181.0/24}} \
                     } {true}{false} }

Create Bootable Windows Installation USB

So, a situation may arise where you are required to install Windows where the only media you have is a USB drive. Now if you're already running Windows, you can do this with the media creation tool.
You can download a Windows 10 image direct from M$ here.

Obviously, we're running Linux ;) So download the ISO image and let's get cracking ;)

USB Prepare

You will need a drive >4Gb (I used a 2Gb :\)

Insert your USB and execute (where 'X' is the drive eg /dev/sda):

fdisk /dev/sdX

I'm assuming there are no partitions defined. So we'll start from scratch.

# fdisk /dev/sdj

Welcome to fdisk (util-linux 2.30.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.


Command (m for help): p
Disk /dev/sdj: 1.9 GiB, 2058354688 bytes, 4020224 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0003dd8d
-------------------------------------------------------------------------------
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p): p
Partition number (1-4, default 1): 
First sector (2048-4020223, default 2048): 
Last sector, +sectors or +size{K,M,G,T,P} (2048-4020223, default 4020223): 

Created a new partition 1 of type 'Linux' and of size 1.9 GiB.
-------------------------------------------------------------------------------
Command (m for help): t
Selected partition 1
Hex code (type L to list all codes): 7
Changed type of partition 'Linux' to 'HPFS/NTFS/exFAT'.
-------------------------------------------------------------------------------
Command (m for help): a
Selected partition 1
The bootable flag on partition 1 is enabled now.
-------------------------------------------------------------------------------
Command (m for help): p
Disk /dev/sdj: 1.9 GiB, 2058354688 bytes, 4020224 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x0003dd8d

Device     Boot Start     End Sectors  Size Id Type
/dev/sdj1  *     2048 4020223 4018176  1.9G  7 HPFS/NTFS/exFAT
-------------------------------------------------------------------------------
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.

Make sure you have sys-fs/ntfs3g installed.

Execute

mkntfs -Q /dev/sdj1
# mkntfs -Q /dev/sdj1
Cluster size has been automatically set to 4096 bytes.
Creating NTFS volume structures.
mkntfs completed successfully. Have a nice day.
Install boot sector

Make sure you have ms-sys installed.

Execute ms-sys -n /dev/sdj1

# ms-sys -n /dev/sdj1
NTFS Windows 7 boot record successfully written to /dev/sdj1

Now for the installation files.

Mount the downloaded ISO and the USB drive.

mount -o loop /$HOME/Downloads/Win10_1709_English_x64.iso /mnt/THIS
mount /dev/sdj1 /mnt/THIS

Now copy the files.

cp -Rv /mnt/THIS/* /run/media///

And that's it. Unmount the ISO/USB and go install.