Month: August 2017

Cleaning Up Old OpenHAB Persistence Tables

So my husband asked for a program that would go out to the OpenHAB persistence database and identify all of the item tables that are no longer associated with active items. If you rename or delete an item from OpenHAB, the associated data is retained in the persistence database. Might be a good thing – maybe you wanted that data. But if it’s useless fluff … well, no need to keep the state changes from a door sensor that’s no longer around.

Wrote the code, and asked him how many days old he wanted the last update to be before the item table got dropped … and he told me this was a useless way to do it and maybe something really hadn’t updated in six months or three years and age of last update is no way to be identifying tables to be removed. Which, yeah, then why ask for it!? Then I needed to write something that takes a list of items from OpenHAB and identifies everything in the items table that does not appear in the OpenHAB list so those tables can be deleted. But I figured I’d post the original code too in case anyone else could use it. Both in perl, and neither in particularly well written perl. I trust the data and don’t want to protect against insertion attacks.

Drop tables for items that no longer appear in OpenHAB:

use strict;
use DBI;

my %strItemsFromOpenHAB = ();
        my $strCurrentItem = $_;
close INPUT;

my $dbh = DBI->connect('DBI:mysql:openhabdb;host=DBHOST', 'DBUID', 'DBPassword', { RaiseError => 1 } );

my $sth = $dbh->prepare("SELECT * FROM items");
while (my @row = $sth->fetchrow_array) {
        my $strItemID = $row[0];
        my $strItemName = $row[1];
        if(! $strItemsFromOpenHAB{$strItemName} ){              # If the current item name is not in the list of items from OpenHAB
#               print "DELETE FROM items where ItemID = $strItemID\n";
                print "DROP TABLE Item$strItemID;  # $strItemName \n";

close OUTPUT;


Identify tables that have not been updated in iTooOldInDays days:

use strict;
use DBI;
use Date::Parse;
use Time::Local;

my $iTooOldInDays = 365;

my $iCurrentEpochTime = time();

my @strItems = ();
my $iItems = 0;

my $dbh = DBI->connect('DBI:mysql:openhabdb;host=DBHOST', 'DBUID', 'DBPassword', { RaiseError => 1 } );

my $sth = $dbh->prepare("SELECT * FROM Items");
while (my @row = $sth->fetchrow_array) {
        $strItems[$iItems++] = $row[0];

for(my $i = 0; $i < $iItems; $i++){ my $strTableName = 'Item' . $strItems[$i]; my $sth = $dbh->prepare("SELECT * FROM $strTableName ORDER BY Time DESC LIMIT 1");
        while (my @row = $sth->fetchrow_array) {
                my $strUpdateTime = $row[0];
                my @strDateTimeBreakout = split(/ /,$strUpdateTime);
                my $strDate = $strDateTimeBreakout[0];
                my $strTime = $strDateTimeBreakout[1];

                my @strDateBreakout = split(/-/,$strDate);
                my @strTimeBreakout = split(/:/,$strTime);

                my $iUpdateEpochTime = timelocal($strTimeBreakout[2],$strTimeBreakout[1],$strTimeBreakout[0], $strDateBreakout[2],$strDateBreakout[1]-1,$strDateBreakout[0]);
                my $iTableAge = $iCurrentEpochTime - $iUpdateEpochTime;

                if($iTableAge > ($iTooOldInDays * 86400) ){
                        print "$strTableName last updated $strUpdateTime - $iUpdateEpochTime\n";

close OUTPUT;

Configuring and Using RPZ

I realized today what, while I had written about why response policy zones are useful, I never indicated how to configure one! So here’s a quick document outlining how to set it up in ISC Bind. In your named.conf file, add a response policy to your options section:

        response-policy {
                zone “rpz”;
Then add the correspondingly named zone at the end of the file. For purposes of testing, I added a zone as a forward only zone so I could perform a network capture to see what exactly transpires when a name in the RPZ is resolved.
zone “rpz” {
      type master;
      file “rpz.db”;
      allow-query { none; };
      allow-transfer { none; };
zone “” {
    type forward;
    forward only;
    forwarders {; };
Then you just have to make a rpz.db where you store your named files:
$TTL 60
$ORIGIN rpz.
@            IN    SOA  localhost. root.localhost.  (
                          2   ; serial
                          3H  ; refresh
                          1H  ; retry
                          1W  ; expiry
                          1H) ; minimum
                  IN    NS    localhost.    CNAME
Restarted named and ran “rndc flush” to avoid serving cached content instead of the RPZ host data. Then ran a few tests and confirmed that the resolution configured in the rpz zone:
[lisa@fedora02 named]# dig +short @localhost
[lisa@fedora02 named]# dig +short @localhost
[lisa@fedora02 named]# dig +short @localhost
In this process, I learnt something interesting about ICS’s implementation of RPZ: it still performs the query and then overrides the results. Odd waste of cycles, but the resolution that was subsequently turned into yahoo’s address from the rpz zone. Looking up a host that isn’t in my RPZ and I got another query out to which was expected. Query to something not in the forward zone and not in the rpz zone and I get no traffic to (because it follows my normal forwarding which is to our ISP’s DNS).
I was curious if this meant rpz could not be used to publish a bad hostname locally – but attempting to resolve a bad hostname (added with the same CNAME to Yahoo and reloaded my zone) worked just fine.

[root@fedora02 ~]# dig @localhost

; <<>> DiG 9.11.1-P2-RedHat-9.11.1-2.P2.fc26 <<>> @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8382
;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 4, ADDITIONAL: 3

; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 1aa34751c5df7f78857a921259a8706fb5e1741a46eb5352 (good)
; IN A


;; AUTHORITY SECTION: 172800 IN NS 172800 IN NS 172800 IN NS 172800 IN NS


;; Query time: 1204 msec
;; SERVER: ::1#53(::1)
;; WHEN: Thu Aug 31 16:24:15 EDT 2017
;; MSG SIZE rcvd: 315

But there is a query that goes out to the name server and a ‘no such name’ result returned. Odd.


Read an interesting take on Mueller’s coordination with NY AG Schneiderman: Trump might manage to shut down Mueller’s investigation (or enough Republicans may OK the 6 month time limit). Justice can suppress documents from the investigation (or oopse them), but it would be a gross abuse of federal power to confiscate (or even assume to control the release of) investigative materials in possession of the NY AG.

Which isn’t to say Trump & co wouldn’t *try* … but something not a lot of Americans seem to know about the lead up to the Revolutionary war: there was quite a bit of debate in Parliament about the same grievances the American colonialists had. Not because Britons had some altruistic concern for the socio-political well-being of Colonials, but if some set of offensive rules apply to THOSE crown citizens today, what is to stop them from applying to ME tomorrow? Point being – it’s one thing to treat women horrendously, screw over poor people, take the David Duke approach to race relations. But if the feds can come in and overrule state criminal investigations in NY today … well, here’s hoping people who like to prance around screaming about states rights have some line they aren’t OK with Trump crossing.


When a law enforcement officer refuses to enforce the law and can rely on the president to pardon the infraction, does that not elevate the executive branch above all others? Trump encouraged law enforcement officers to abuse people in their custody (full on Jerry Seinfeld why are they so careful getting the guy in the back of the cruiser?). Can police officers expect to be pardoned when charged with brutality? Will they be guaranteed their jobs even after killing a suspect? What is the point of having laws (or a court system) if one branch of the government can decide what should be legal by pardoning infractions they don’t like.

Sure there are mitigating circumstances that may diminish the severity of the infraction, mandatory sentencing produced people whose ‘time’ in no way fit their ‘crime’, some people are amazingly reformed after the commission of their crime. Hell, the law could change five years into your term rendering your infraction a perfectly legal activity. Some people deserve a break. Would it not be better to allow the judicial system to redress these scenarios? Mechanisms to do so are already in place — parole reduces the effective sentence, but the individual retains a criminal record. Expunction seals the record and generally means the individual does not need to disclose the conviction.

And not that I believe Trump is in the least bit concerned about people whose rights were trampled by Arpaio? What redress do they have? The purported ideology does not care who is damaged in the pursuit of illegal immigrants. And that is what makes this particular pardon so offensive. In the ‘war on drugs’, cops abused people in poor neighborhoods. Evidence was planted to convict those who were known to be dealers. What would have happened if Reagan had just pardoned those officers?

We watched a lot of movies when building Windstream. A lot. We were splitting an eDirectory tree, and a couple of servers each night would be moved. I ran the support bridge for the split, and needed a way to get a good number of people to volunteer to work 6p-2a six days a week with no overtime. Accomplished this by having free dinner, snacks, and movies. I didn’t own that many DVDs, so people volunteered to bring their collection too. It was a lot of fun (some of the techs ringing into the bridge would get the same DVD going and watch with us), and I got to see a lot of movies I wouldn’t have known existed. One of those movies was an early 70’s movie based on a mid 60’s book: Colossus: The Forbin Project. Pardoning Arpaio makes me think of the movie — the premise was essentially that the US government commissioned a computer system to control the country’s nuclear arsenal with the goal of preserving world peace because computers do not suffer human foibles such as irrational emotional responses. The computer accomplished it’s goal … by using the nuclear arsenal to blackmail humanity into peace (either you stop fighting or I start nuc’ing you until you concede). Same lack of constraint or morality. It doesn’t matter how many people are tortured or killed, as long as the objective is met.

Avocado Chocolate Mousse

Mmmmm – I made an avocado chocolate mousse that a friend of a friend sent out. I modified the original recipe significantly: used a lot of carob in place of the cocoa, used maple syrup in place of honey, and used coconut milk in place of almond milk


2 avocados (room temperature)
6 Tbsp coconut milk
5 Tbsp carob powder
1 Tbsp cocoa powder (I used KAF’s triple cocoa powder)
1/2 tsp vanilla extract
2 Tbsp maple syrup


Dice up the avocado into large chunks. Combine everything in a food processor and process until smooth and creamy. Eat 🙂


Microsoft’s latest security newsletter included the fact that more than 90% of Fortune 500 companies have not fully implemented DMARC. Wow — that’s something I do at home! Worse still, the Fortune 500 company for which I work is in that 90% … a fact I hope to rectify this week. SPF is just some DNS entries that indicate the source IPs that are expected to be sending email from your domain. Lots of SPF record generators online.

DKIM is a little more involved, but it’s a lot easier now that packages for DKIM are available on Linux distro repositories. You still *can* build it from source, but it’s easier to install the OpenDKIM package.

Once the package is installed, generate the key(s) to be used with your domain(s).

cd /etc/opendkim/keys/
openssl genrsa -out dkim.private 2048
openssl rsa -in dkim.private -out dkim.public -pubout -outform PEM
# secure private key file
chown opendkim:opendkim dkim.private
chmod go-r dkim.private

Decide on the selector you are using — I use ‘mail’ as my selector. At work, I use ‘2017Q3Key’ — this allows us to change to a new key without in-transit mail being impacted. Old mail was sent with the 2017Q2 selector and *that* public key is in DNS. New mail comes across with 2017Q3 and uses the new DNS record to verify. I do *not* share these keys – anyone else sending mail from our domain needs to generate their own key (or I make one for them), use their own unique selector, and I will create the DNS records for their selector. When marketing engages a third party to send e-mails on our behalf, we have a 2017VendorName selector too.

Edit /etc/opendkim.conf. The socket line is not necessary – I just tend away from default ports as a habit. Since it’s bound to localhost, not such a big deal.

Mode sv
Socket   inet:8895@localhost
Selector mail
KeyFile /etc/opendkim/keys/dkim.private
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
InternalHosts refile:/etc/opendkim/TrustedHosts

There’s a config option to “SendReports” — it’s a boolean that indicates if you want your system to send failure reports when the sender indicates they want such reports and provide a reporting address. Especially for testing purposes, I recommend indicating your domain wants reports — it is helpful in case you’ve got something configured not quite right and are failing delivery on some messages. As such, configure my installation to send reports. It’s additional overhead in cases where verification fails; I don’t see all that many failures, and it isn’t a lot of extra load. Since I know my installation will send detailed failure information, I can use my domain when testing new implementations.

Once you have the base configuration set, edit /etc/opendkim/SigningTable and add your domain(s) and the appropriate selector


Edit /etc/opendkim/KeyTable and map each selector from the SigningTable to a key file

Edit /etc/opendkim/TrustedHosts and add the internal IPs that relay your domain’s mail through the server (IP addresses or subnets)

Create DNS TXT records – the part after p= is the content of the public key file for that selector. When you are first setting up DKIM, use t=y (yes, we are just testing this). Once you confirm everything is functional, you can change to y=n (nope, really pay attention to our DKIM signature and policy). The policy is an individual preference. I use ‘all’ (all mail from my domain will be signed) and “o=-” (again all mail from my domain will be signed). You can use “o=~” (some mail from my domain is signed, some isn’t … who knows) and “dkim=unknown” (again, some is signed). You can use “dkim=discardable” (don’t just consider the message as more likely to be spam if it is not signed … you can outright drop the message). As a business, I don’t use this *just in case*. Something crazy happens – the dkim service falls over, your key gets mangled – and receiving parties can start dropping your messages. Using “dkim=all” means they are more apt to quarantine them as spam, but someone can go and get the messages. And hopefully notice something odd is happening.

mail._domainkey.domain.tld  TXT k=rsa;t=y;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzTnpc7tHfyH1zgT3Jx/JHmGSz8WCy1jvzu5QsYvDBmimKEHRY4Kz4mya5bOYsDQuJ/sz+BJo6xDwsUXCuyEkykIlgqP+7E9oK2EcW0dZms87SGmNEnNBN5iTe0pdzk1lXx2js3QdOWswO+cmA9F1Z8OzSR+2u79huugPFBHl79zFvOEHbigrmeHEfo0KHWpeNomf/xKx+wyYr1n3R5gS+28CeC3abSyKgmaYYRLoZsjrCLbEM0m2YPJRKd1ZGOObBMa4PZWj7pT07ISEjoNnXQ27BtcL/QjKKeLkbJ0UGEOSdPEJKuEpAUvYU9lA5hbtzrqiwdlPxWYocDVPrcqAHwIDAQAB
_adsp._domiankey.domain.tld TXT dkim=all TXT t=y;o=-; TXT t=y;dkim=all


Edit /etc/mail/ (using the port defined in /etc/opendkim.conf

INPUT_MAIL_FILTER(`dkim-filter’, `S=inet:8895@localhost’)

Make your to and verify that you’ve got the dkim-filter line

Xdkim-filter, S=inet:8895@localhost

Start opendkim, then restart sendmail. Now test it — inbound mail should have *their* DKIM signatures verified, outbound mail should be signed with the appropriate key.

Once you have verified your DKIM is functioning properly — well, first of all you can update your DNS records to remove testing mode. Then create your DMARC record:     v=DMARC1; p=quarantine; sp=quarantine;!10m;!10m; rf=afrf; pct=100; ri=172800
 Again, you don’t need to use quarantine — ‘reject’ would recommend mail be dropped or ‘none’ recommends no action (good for testing). The rua (aggregate reporting email address) and ruf (address to recieve failing samples for analysis) should be in your domain.
You could add either/both “adkim=s” or “aspf=s” to indicate your DKIM or SPF adhere to strict standards. I use relaxed (default, do not need to specify it in the TXT record).
If you want the reports delivered to an address outside of your domain, that domain needs to publish a DNS record authorizing receipt of the reports:     v=DMARC1

Sendmail VirtUserTable

Some mail systems support sub-addressing (i.e. user+ignoredstring@domain.tld), but Exchange is not one of them. Instead of trying to sort out sub-addressing through sendmail or Exchange, we use sendmail’s virtusertable to map entire subdomains (i.e. over to our primary e-mail addresses. If an address becomes compromised, we can blacklist the particular address in the access table).

Virtual Domain Aliases

These aliases allow changes to be made to intended recipient addresses.  There are two files required for an address to be aliased.  An entry for “VIRTUSER_DOMAIN_FILE” will exist in the specifying the file listing the domains to be included for aliasing.  For us, this is /etc/mail/virtuser-domains.  This is a text file containing the name of each domain to be virtualized for aliasing, one domain per line.  Please note, the domains included herein need only be the recipient domains, not the domains to which aliases are mapped.  E.G. our virtuser-domains file contains just:

And yet we can alias to … it is only the source address that needs to be defined in virtuser-domains.

Aliases for the virtual domains are contained in /etc/mail/virtusertable.  The left-hand entry is the recipient address and the right-hand entry is what that recipient will be translated to.  Left-hand entries can be an email address ( or a domain (

Right-hand entries can be an alternate address.  If the address should remain the same, an exclamation point can be used:            !

The right-hand entry can also be an action, like error which will return an error code            error:nouser User unknown


To commit changes to the virtusertable:

makemap hash /etc/mail/virtusertable.db < /etc/mail/virtusertable


Testing Virtual Aliases:

You can test the results of the virtual address space aliasing using sendmail –bt.  From within the new prompt (a greater than sign on a blank line) type3,0 followed by the address you would like to test.  E.G.:

[uid@NEOHTWNLX821 ~]# sendmail -bt
ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)
Enter <ruleset> <address>
> 3,0
canonify           input: llanders @ windstream . com
Canonify2          input: llanders < @ windstream . com >
Canonify2        returns: llanders < @ windstream . com . >
canonify         returns: llanders < @ windstream . com . >
parse              input: llanders < @ windstream . com . >
Parse0             input: llanders < @ windstream . com . >
Parse0           returns: llanders < @ windstream . com . >
ParseLocal         input: llanders < @ windstream . com . >
ParseLocal       returns: llanders < @ windstream . com . >
Parse1             input: llanders < @ windstream . com . >
Recurse            input: llanders @ valortelecom . com
canonify           input: llanders @ valortelecom . com
Canonify2          input: llanders < @ valortelecom . com >
Canonify2        returns: llanders < @ valortelecom . com . >
canonify         returns: llanders < @ valortelecom . com . >
parse              input: llanders < @ valortelecom . com . >
Parse0             input: llanders < @ valortelecom . com . >
Parse0           returns: llanders < @ valortelecom . com . >
ParseLocal         input: llanders < @ valortelecom . com . >
ParseLocal       returns: llanders < @ valortelecom . com . >
Parse1             input: llanders < @ valortelecom . com . >
Mailertable        input: < valortelecom . com > llanders < @ valortelecom . com . >
Mailertable        input: valortelecom . < com > llanders < @ valortelecom . com . >
Mailertable      returns: llanders < @ valortelecom . com . >
Mailertable      returns: llanders < @ valortelecom . com . >
MailerToTriple     input: < > llanders < @ valortelecom . com . >
MailerToTriple   returns: llanders < @ valortelecom . com . >
Parse1           returns: $# esmtp $@ valortelecom . com . $: llanders < @ valortelecom . com . >
parse            returns: $# esmtp $@ valortelecom . com . $: llanders < @ valortelecom . com . >
Recurse          returns: $# esmtp $@ valortelecom . com . $: llanders < @ valortelecom . com . >
Parse1           returns: $# esmtp $@ valortelecom . com . $: llanders < @ valortelecom . com . >
parse            returns: $# esmtp $@ valortelecom . com . $: llanders < @ valortelecom . com . >

Clean Coal

Running freshly mined coal chunks through a waterfall to wash off dirt doesn’t seem like it would be much less effective than proposed carbon sequestration methods.
Seriously – plant a bunch of trees downwind of the coal fire == sequestration. Capture and compress these emissions then injected them into the Earth … what could go wrong with that? Combine it with some other elements to make limestone. Split CO2 molecules, released pure O2 into the air & then made diamonds or carbon nano-tubes. Whatever “clean the coal emissions” approach you want to go with, you still have mercury, cobalt, arsenic, hydrofloric acid, cadmium, beryllium, chromium, lead, manganese … all sorts of pollutants escaping into the atmosphere. Spent a lot of money to get there, created public perception that environmental impact had been negated, and if my rudimentary understanding of the proposed sequestration methods is anywhere near accurate the energy output of the facility is reduced as well (i.e. I need to burn MORE coal w/ sequestration to produce the same amount of energy).
Plus dirt – not generally combustible stuff. Wouldn’t dirt already be washed off ’cause I’m not paying per tonne or whatever to get a bunch of non-burny stuff?

Sendmail Mailertable

Mailertable (/etc/mail/mailertable)

Routing information for external delivery. Functionally, these are like the SMTP Connectors within Exchange. The mailertable entries can override everything including smarthost definitions. This is required for internal mail routing – our sendmail servers should not transmit email for to the MX records but rather the destination we intend. We also use mailertable entries to force B2B communication over internal secured channels.

If a server is unable to deliver mail to a specific domain (e.g. one of our public IP addresses gets blacklisted), a mailertable entry can be used to direct all mail destined for the domain through one of our servers still able to make delivery.

The file contains two columns, domains and actions. Domains can be ends-with substring matches:

Will match as well as Domains can also be a full match of the right-hand side of the email address:

Which will match The most “accurate” match will win, not just the first match in line. So if your file contains the following: relay:[] relay:[]

Mail destined for will be sent to

Actions contain both a mailer and a host. The mailer can redirect messages to local users: local:username

Or it can force an error response: error:5.3.0:Unknown User

Our use of the mailertable, though, is to redirect mail destined for the domain: relay:[] relay:[]

In these cases, the square brackets around the destination override the MX record. To reroute a domain’s delivery destination, then, it is imperative that the host be enclosed in square brackets.

To commit changes to the file, either use “make” from within /etc/mail to commit all changes or the following command to commit just the changes to mailertable:
makemap hash /etc/mail/mailertable < /etc/mail/mailertable

Sendmail Configuration

Sendmail Configuration Files – and


Sendmail configuration files are located by default in /etc/mail/.  PureMessage uses /opt/pmx4/sendmail/etc/mail/


The main configuration file is  This is a rather cryptic file which we will not configure directly.  If you want to know the syntax for, read the doc at or get the O’Reily book.  This information is specific to the MC file from which a macro builds the CF file.. contains instructions to allow the M4 macro processor to build  Very important, before you can use a macro to create a file, you need to have the macro installed.  This is the sendmail package sendmail-cf.  To ascertain if the package has been installed on RedHat:


[root@LJLLX001 mail]# rpm -qa | grep sendmail




Both sendmail and sendmail-cf packages should appear in the results.  If you do not have the CF package, install it.


The text “dnl” within denotes a comment – like a tic in VisualBasic or a hash in perl.  Many lines end with dnl, or dnl with some type of commentary.  Lines beginning with dnl are not processed.


Common instructions within a file:



This line refers the m4 utility to the correct “translation” to build the file.  Important that the line is at the top of the mc file, but nothing to do with sendmail configuration specifically


VERSIONID(`setup for Red Hat Linux’)dnl

This line is not required, and we have ‘junk’ in it frequently.  It records the version of sendmail in the cf file for administrative reference.



More instructions for m4, different OS’s have different locations for sendmail files and the OS defined here identifies which parameters to use.  This line again needs to be at the top of the mc file



Defines which user and group sendmail will run as – do NOT pick root here.  User id 8 (mail) and group id 12 (mail) from /etc/passwd and /etc/groups respectively.


define(`confTO_CONNECT’, `1m’)dnl

Time limit for SMTP connection timeout, set to one minute normally.  This is how long your server will wait for an initial connect() to complete.



Email is normally routed by MX records.  This instruction means the ‘domain’ can also be a host name with no MX defined.  E.G.  sending email to will return the MX records, as they exist.  Attempting to email will return no MX records, but LX810 will be contacted directly to attempt delivery.  This is a most useful instruction for return delivery to system mailers.



The sendmail class w lists the host and IP addresses for which sendmail accepts and takes local delivery.  This class can be automatically populated, or using this directive not automatically populated.  We configure this information manually in other files.


You can use a sendmail command line to determine what is set to various system variables:

[root@LJLLX001 ~]# sendmail -d0.1 -bv

Version 8.13.1





============ SYSTEM IDENTITY (after readcf) ============

(short domain name) $w             =      LJLLX001

(canonical domain name) $j        =

(subdomain name) $m                 =

(node name) $k                             =




Exactly what it says – the location of procmail


define(`ALIAS_FILE’, `/etc/aliases’)dnl

Location of the file for local delivery aliases – not something we use often as there are few local delivery accounts.  In the ISP, this file can be used to give someone additional addresses which deliver to the same mailbox.  This file can also be used to direct delivery of a local account to a program – in PureMessage for example, /opt/pmx4/sendmail/etc/mail/aliases directs the pmx-auto-approve address to the application which releases user messages.


define(`confBIND_OPTS’, `WorkAroundBrokenAAAA’)dnl

This is a resolver option, it instructs sendmail to ignore SERVFAIL errors during an IPv6 lookup.  We had a few domains for which we could not deliver mail without this directive.


define(`SMART_HOST’, `[]’)

A smart host can be used instead of direct mail delivery.  For a server which is not meant to deliver mail to the internet (neohtwnlx824 for instance) the smart_host directive sends all mail to the defined destination.  The destination can be a hostname or an IP address.  Note, the mailertable will override the smarthost.


define(`STATUS_FILE’, `/var/log/mail/statistics’)dnl

Retains statistical information on server – use the command mailstats to output the statistics, the file created here is not text


define(`UUCP_MAILER_MAX’, `2000000′)dnl

Maximum size for messages relayed by UUCP mailers


define(`confPRIVACY_FLAGS’, `authwarnings,novrfy,noexpn,restrictqrun’)dnl

Disables unwanted commands – usually for security reasons.  EXPN expands groups into component members, for instance, so NOVRFY is used to disable the command.  Some of these are more important if local delivery is handled by the sendmail server.


define(`confAUTH_OPTIONS’, `A’)dnl

What kinds of authentication are supported by the server.  Useful if you are requiring authentication to relay mail, we do not do this.  Some UNIX hosts get confused if AUTH is an option made available, and you need to remark this line out of the mc file.


define(`confTO_QUEUEWARN’, `6d’)dnl

If you ever see an email from a destination mail server saying it is still trying to deliver your message and just wanted to let you know – that is what this interval defines.  To truly adhere to RFC specifications, a sendmail server should continue to attempt delivery for at least four to five days.  As a “nice” feature, the server can send periodic notifications to the sender that delivery has been delayed.  This standard comes from a time when circuits were smaller and quite lossy.  It could reasonably take days to establish a connection to the destination and transmit a message.

We are rogue and just return mail as undeliverable after a shorter period.  No reason to notify users, but to ensure that a notification is not sent, we put the warning interval at something higher than the expiration interval.


define(`confTO_QUEUERETURN’, `12h’)dnl

Related to the QUEUEWARN interval – this is the period after which the sendmail server considers the message undeliverable and returns it to the sender.  By default, this is five days so we make sure to define something more reasonable.  Otherwise there would be no way to identify “high” mail queue counts for alerting.


define(`confQUEUE_LA’, `16′)dnl

Load average at which queue only functionality is engaged


define(`confREFUSE_LA’, `48′)dnl

Load average at which SMTP connections are refused


define(`confDELAY_LA’, `30′)dnl

Load average at which sendmail will delay one second on SMTP commands


define(`confMIN_QUEUE_AGE’, `5m’)dnl

Minimum time a message has to sit in the queue before it is retried


define(`confTO_HOSTSTATUS’, `2m’)dnl

If a host has been denoted as unavailable, the status will be cached for this duration.  After the interval expires, connection to the host will be retried


define(`confMAX_DAEMON_CHILDREN’, 2000)

Maximum number of children processes permitted.  Sendmail will reject subsequent connections once this number has been reached.  Very important to have something defined on the DMZ servers.  Default is infinite and it is possible for a server to become unresponsive and need to be rebooted with out of memory errors when too many processes are spawned.


define(`confTO_IDENT’, `0′)dnl

Timeout for responses to IDENT



The default MSA options are not used, but rather explicitly defined in the DAEMON_OPTIONS directive



Shell used for command line mailing programs, not really pertinent in our case


FEATURE(`mailertable’,`hash -o /etc/mail/mailertable.db’)dnl

This file will be discussed in more detail later, this directive specifies the use of a mailertable and the location of the file.



This file will be discussed in more detail later, this directive specifies the location of the file containing virtualised domains


FEATURE(`virtusertable’,`hash -o /etc/mail/virtusertable.db’)dnl

This file will be discussed in more detail later, this directive specifies the use of virtual user mapping and the location of the file containing said mappings



Appends the local host domain to even locally delivered mail.



Alternate host names are in /etc/mail/local-host-names – machine aliases



Users who can set alternate envelope from addresses without generating a warning message.  File is /etc/mail/trusted-users


FEATURE(local_procmail,`’,`procmail -t -Y -a $h -d $u’)dnl

Specifies program to use as the local mailer, and command options


FEATURE(`access_db’,`hash -T<TMPF> -o /etc/mail/access.db’)dnl

This file will be discussed in more detail later, this directive specifies the use of an access restriction table and the location of the file.





DAEMON_OPTIONS(`Port=smtp, Name=MTA’)dnl

This is where the settings for the MSA are defined.  Port=smtp uses the default port of 25, or an alternate port can be used.  Addr=# can be included to bind sendmail to a specific address (including for localhost access only).



Defines a “milter” – mail filter.  The port and destination of the milter must be included with S=.  S=inet is a IPv4 socket, S=inet6 is an IPv6 socket, and S=local is a Unix-domain socket (/var/run/)

F= defines an action to take on failure, R (reject), T (tempfail), or if no option is included just pass the message through sendmail and ignore the milter

T= defines timeouts for sendmail’s communication with the milter:

C         Connect timeout

S          Sending timeout (sendmail transmission of data to milter)

R          Reading timeout (for reply from milter)

E          Overall timeout (between sending end of message and final ack)







This group of directives are all interrelated.  Masquerading is basically replacement – MASQUERADE_AS is the domain which will be used in place of the domains identified in MASQUERADE_DOMAIN lines.  In this case, both and @homedomain.local will be overwritten with  The directive FEATURE(masquerade_entire_domain) could be included to replace any subdomain of the masquerade domains (e.g.,, and in addition to

Masquerade envelope applies the masquerade to the envelope information and allmasquerade applies the masquerade to everything in the envelope, including cc:, from: and to: — this directive is important when we mask an acquired company’s email domain with our own.



Allows the use of domains in the MAIL FROM command to be invalid network and sender domains.  Since some people do not manage to configure their mail servers properly, we are less restrictive here to avoid complaints.



Domain(s) for which the server will accept local delivery – since our servers do not really deliver mail the domain should include the localdomain to prevent accidental misdirection of mail




Defines mailers to be used in addition to local – these should be the last lines of the mc file




When you make changes to the file, you will need to run the macro processor to update the CF file.  You can see the results by running:

m4 | less


The text which will be used in will be displayed on the screen.  To actually commit the changes, use:

m4 >

or just type



Make will update all of the files in /etc/mail, so ensure you like all the changes you have made, not just the changes to