Skip to main content

You are here

CentOS

Send Email from a Shell Script Using Gmail’s SMTP

In my previous post, I enabled my local mail server to relay all outgoing mail to Google's SMTP servers. However if you want to completely bypass using any sort of MTA, then you will only need to configure your Mail User Agent client to use Gmail STMP settings directly.

In Linux, I've always used the mailx utility to send out email messages from the command line or from a shell script. By default, mailx uses the local mail server to send out messages, but configuring it to use a custom SMTP server is extremely easy.

Inside a shell script configuration would look like this:

to="[email protected]"
from="[email protected]"
email_config="
-S smtp-use-starttls \
-S ssl-verify=ignore \
-S smtp-auth=login \
-S smtp=smtp://smtp.gmail.com:587 \
-S from=$from \
-S [email protected] \
-S smtp-auth-password=ULTRASECUREPASSWORDHERE \
-S ssl-verify=ignore \
-S nss-config-dir=/etc/pki/nssdb \
$to"

echo "Test email from mailx" | mail -s "TEST" $email_config

To have the mail settings configured to be used by mailx from the command line simply set your settings in ~/.mail.rc

set smtp-use-starttls
set ssl-verify=ignore
set smtp=smtp://smtp.gmail.com:587
set smtp-auth=login
set [email protected]
set smtp-auth-password=ULTRASECUREPASSWORDHERE
set from="[email protected]"
set nss-config-dir=/etc/pki/nssdb

References:
https://www.systutorials.com/1411/sending-email-from-mailx-command-in-linux-using-gmails-smtp/

Linux: 

Awesome Applications: 

Configuring Postfix to use Gmail

Configuring Postfix to use Gmail as the outgoing SMTP relay endpoint is a relatively simple process. I’m my case, I’m not using an @gmail.com account. Rather, since all of my domains use G Suite, I’ve created a special dedicated email account that I’ll be using to send out email from.

Before starting configuring Postfix, it is important that you enable "Less secure app access" on the Gmail account that you will be configuring to send outgoing messages.

I’m using CentOS 7.x as my mail server OS. These were the steps I used to configure Postfix.

1. Install necessary packages:

yum install postfix mailx cyrus-sasl cyrus-sasl-plain

2. Create /etc/postfix/sasl_passwd file with the your authentication credentials:

[smtp.gmail.com]:587    [email protected]:mypassword

3. Update file permissions to lockdown access to our newly created authentication config file:

chmod 600 /etc/postfix/sasl_passwd

4. Use the postmap command to compile and hash the contents of sasl_passwd:

postmap /etc/postfix/sasl_passwd

5. Update /etc/postfix/main.cf

relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_security_options =
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt

6. Finally, enable and restart postfix:

systemctl enable postfix
systemctl restart postfix

Lastly, although it’s not needed to get a working Postfix to Gmail STMP config working. I would recommend enabling outgoing throttling. Otherwise Google might temporarily suspend your account from sending messages!

Additional /etc/postfix/main.cf update:

smtp_destination_concurrency_limit = 2
smtp_destination_rate_delay = 10s
smtp_extra_recipient_limit = 5

In my case, I configured Postfix to only handle two concurrent relay connections, wait at least 10 seconds to send out the email and set the recipient limit to 5 (per queue message session).

NOTE: As I mentioned, since I'm not using an @gmail.com, I had to add an SPF DNS record so that the outgoing emails pass all of Google’s spam tests.

DNS txt record:

v=spf1 include:_spf.google.com ~all

Example received email header that was sent from the newly Postfix to Gmail smtp configuration:
Passing Gmail Email Header

To conclude, it is import to remember that this Postfix configuration will overwrite whatever "From" source set by your mail user agent (as the above email header image demonstrates).

Resources:
https://www.howtoforge.com/tutorial/configure-postfix-to-use-gmail-as-a-mail-relay
https://wiki.deimos.fr/Postfix:_limit_outgoing_mail_throttling.html

Linux: 

Awesome Applications: 

Compiling Nginx dynamically loadable modules

I’ve compiled plenty of dynamic loadable modules for Apache, but never for Nginx up until now. I must say compiling modules for Apache is a much easier process compared to Nginx.

I use Nginx as a SSL reverse proxy/terminator. I wanted to be able to remove certain http headers at the Nginx level rather than on my Apache backend. The way to achieve this is using a module called headers more , which is not part of core Nginx.

My Setup:
I’m using the nginx package installed via by the epel repository for CentOS 6.x. So compiling the module was a bit tricky initially.

Compilation Process:
1). Download the source code for the module, from https://github.com/openresty/headers-more-nginx-module/tags

2). You need to find out the running version of Nginx:

$ nginx -v
nginx version: nginx/1.10.1

3). Download and extract the source code for that version of Nginx:

$ wget 'http://nginx.org/download/nginx-1.10.1.tar.gz'

4). Compilation:
On my first initial compilation, I mistakenly compiled the module using the following:

./configure --prefix=/opt/nginx \
--add-dynamic-module=/opt/headers-more-nginx-module/headers-more-nginx-module-0.32

Even though the module compiled successfully, when trying to load the module, it gave me the following error:

$ nginx -t
nginx: [emerg] module "/usr/lib64/nginx/modules/ngx_http_headers_more_filter_module.so" is not binary compatible in /etc/nginx/nginx.conf:9
nginx: configuration file /etc/nginx/nginx.conf test failed

It turns out the module has to be compiled using the same ./configure flags as my running version of Nginx, in addition to the --add-dynamic-module=/opt/headers-more-nginx-module/headers-more-nginx-module-0.32 flag. Luckily, this can easily been obtained by running the following:

$ nginx -V
nginx version: nginx/1.10.1
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-17) (GCC)
built with OpenSSL 1.0.1e-fips 11 Feb 2013
TLS SNI support enabled
configure arguments: --prefix=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_geoip_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' --with-ld-opt=' -Wl,-E'

This is the tricky and annoying part. Since, the epel repo packaged binary of nginx was compiled with support of additional modules, the identical manual compilation that I was trying to do required some dependencies to be installed. In my case I just had to installed the following dependencies:

# yum install libxslt-devel perl-ExtUtils-Embed GeoIP-devel

After compilation completes. Then it's just a matter of adding the module to the nginx.conf config. In my case, I manually copied over the compiled module to /usr/lib64/nginx/modules, so my config looks like this:

load_module "/usr/lib64/nginx/modules/ngx_http_headers_more_filter_module.so";

It is worth mentioning that Nginx is very picky on the order in which its configuration is set. For example. load_module option has be set before the events configuration block like this:

load_module "/usr/lib64/nginx/modules/ngx_http_headers_more_filter_module.so";

events {
    worker_connections  1024;
}

Otherwise, you'll get the following error when trying to load the module.

# nginx -t
nginx: [emerg] "load_module" directive is specified too late in /etc/nginx/nginx.conf:14
nginx: configuration file /etc/nginx/nginx.conf test failed

One really important thing to keep in mind, is that you'll need to recompile the module each time Nginx gets updated.

Linux: 

Awesome Applications: 

Setting up Graphite on CentOS 6.x gotcha

I installed graphite-web via the EPEL repo, and I was getting an 500 error when accessing the Graphite web interface.
Error log:

[Sat Sep 12 00:56:27 2015] [error] [client 192.168.1.21] mod_wsgi (pid=17318): Exception occurred processing WSGI script '/usr/share/graphite/graphite-web.wsgi'.
[...]
[Sat Sep 12 00:56:27 2015] [error] [client 192.168.1.21] File "/usr/lib/python2.6/site-packages/django/db/backends/sqlite3/base.py", line 344, in execute
[Sat Sep 12 00:56:27 2015] [error] [client 192.168.1.21] return Database.Cursor.execute(self, query, params)
[Sat Sep 12 00:56:27 2015] [error] [client 192.168.1.21] DatabaseError: attempt to write a readonly database

Fix: It turns out the sqlite3 database file Graphite write's too, was own by root. So it was simply a matter of updating the ownership to what ever user Apache is running under, in my case it's apache.

chown -R apache.apache /var/lib/graphite-web/

Linux: 

Awesome Applications: 

Server Move and Upgrades!

My little corner of the internet has a new home. My old $29.99 8GB RAM, 3.40GHz Intel Core i3 dedicated server was simply not enough to handle my server needs. Which apparently OVH doesn't even provide that service anymore. So instead I hoped to their mid-tear dedicated service service branch they call So you Start. I opted with their $49.00 SYS-IP-2 service. Now my server's specs is a follows:

  • 2.66 GHz+ Intel Xeon W3520 (4 cores/ 8 threads)
  • 32 GB ECC
  • 2 x 2 TB SATA drives (Software RAID)

I would've love the drives to be SAS and the RAID to be hardware based, but it's definitely not a deal breaker, and just $49.99 a month, it's not much to complain about.

CentOS 6 to CentOS 7 upgrade:
My server migration was fairly straight forward for the most part. I opted to re-create the KVM hypervisor and its guests from scratch. Mainly because I wanted to upgrade all of guests and host from CentOS 6 to CentOS 7. This is where I encountered my first problem. Since I rely on custom nat PREROUTING/POSTROUTING iptables firewall rules for my VMs to properly be able to talk to each other and to the internet. I realized CentOS 7 defaults to firewalld, so instead of trying to rewrite my firewall rules to be compatible with firewalld, I decided to continue to use CentOS 6 on my host operating system, and only upgrade my guests VMs to CentOS 7.

On a side note, my previous guest VMs were originally using raw image format (default cache settings) for its storage, and by god what a hell of a difference it makes changing to use native block storage via LVM. I/O performance on my old server was terrible, the I/O wait percentage was roughly about 6%, now it's less than 1%. Even with the software raid, I/O performance is much better on my new server.

PHP 5.3 to 5.6 upgrade:
Since I don't have anything heavily customized on any of sites, the PHP version upgrade was practically painless.

Apache 2.2 to 2.4 upgrade:
Luckily, upgrading Apache wasn't a big hassle. Anyone considering upgrading from 2.2 to 2.4, it's definitely worth checking out the official upgrade documentation since dropping the old 2.2 configs in onto a 2.4 environment won't work off the gecko. In my case all of my sites were returning 403 forbidden replies and non of my .htaccess files weren't being read by Apache. The fix was really simple.

                AllowOverride All
                Require all granted
   

I must say, I really like Apache 2.4 new authorization syntax. What used to be a three line configuration is now a single line configuration, and much more human readable.

Future Upgrade Plans:
I didn't tackle this during the server migration, but I'll definitely going to be upgrading to Varnish 4 and use PHP FastCGI via php-fpm and mod_proxy_fcgi.

Programming: 

Linux: 

Awesome Applications: 

Networking Quirk in CentOS 7 - Virtual IP not being assigned

I just realized the order of which the IP configurations are set in the /etc/sysconfig/networking-scripts/ifcfg-* file does matter.
For example the following config was failing to assign the virtual IP 192.168.100.218 to one of my systems:

TYPE="Ethernet"
BOOTPROTO="static"
IPADDR=192.168.100.218
NETMASK=255.255.255.0
DEVICE="ens3:1"
NAME="ens3:1"
ONBOOT="yes

Systemd was spitting out the following errors:

Jun 14 01:04:19 webapps network: RTNETLINK answers: File exists
Jun 14 01:04:19 webapps network: RTNETLINK answers: File exists
Jun 14 01:04:19 webapps network: RTNETLINK answers: File exists
Jun 14 01:04:19 webapps network: RTNETLINK answers: File exists

Fix: It turns out that the DEVICE and NAME declaration needs to be assigned and specified before the networking information.

DEVICE="ens3:1"
TYPE="Ethernet"
NAME="ens3:1"
BOOTPROTO="static"
IPADDR=192.168.100.218
NETMASK=255.255.255.0
ONBOOT="yes"

Linux: 

Automated SSL certificate expiration check

It is quite simple to automate checking for near expiring SSL certificates in CentOS. This is accomplished using the certwatch tool. This tool is part of the crypto-utils package.

yum install crypto-utils

Installing crypto-utils, will create the following cron job, /etc/cron.daily/certwatch. By default the /etc/cron.daily/certwatch script only checks for SSL certificates loaded by Apache (httpd -t -DDUMP_CERTS). So Apache users don't have to do any additional config changes to in order to automate the check of near expiring SSL certificates.

Since in https://www.rubysecurity.org I use Nginx as a SSL termination proxy for an Apache backend webapp on a different machine. I had to manually update the /etc/cron.daily/certwatch script to point to my SSL certificates directly.

    #certs=`${httpd} ${OPTIONS} -t -DDUMP_CERTS 2>/dev/null | /bin/sort -u`
    INCLUDE_CERTS='/etc/nginx/certs/*.crt'
    certs=`ls $INCLUDE_CERTS 2>/dev/null`

Here is an example of an expired SSL certificate alert

[[email protected] certs]# certwatch /etc/nginx/certs/www.rubysecurity.org_2014/www.rubysecurity.org.crt
To: root
Subject: The certificate for www.rubysecurity.org has expired

################# SSL Certificate Warning ################

Certificate for hostname 'www.rubysecurity.org', in file (or by nickname):
/etc/nginx/certs/www.rubysecurity.org_2014/www.rubysecurity.org.crt

The certificate needs to be renewed; this can be done
using the 'genkey' program.

Browsers will not be able to correctly connect to this
web site using SSL until the certificate is renewed.

##########################################################
Generated by certwatch(1)

certwatch is far from perfect. It doesn't have any verbose output when doing a check, it solely relies on its exit status to verify if the check was successful. Excerpt from the man page is somewthat appalling.

DIAGNOSTICS
The exit code indicates the state of the certificate:

0
The certificate is outside its validity period, or approaching expiry

1
The certificate is inside its validity period, or could not be parsed

Linux: 

Awesome Applications: 

System Update using Ansible

CentOS

ansible centosbox -m yum -a 'name=* state=latest'

Debian

ansible debianbox -m apt -a 'update_cache=yes name=* state=latest'

Linux: 

Awesome Applications: 

Can't locate Time/HiRes.pm CPAN error on CentOS 7

So the default Perl installation that ships with CentOS 7 minimal install does not include Time::HiRes, which is necessary if you want to use CPAN.

Error:

Can't locate Time/HiRes.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 /root) at /usr/share/perl5/Net/Ping.pm line 313.

Fix:

yum install perl-Time-HiRes

Programming: 

Linux: 

CentOS: Apache - Directory index forbidden by Options directive

By default, the CentOS Apache configuration does not allow index directory listings. So I enabled Indexes Option's for the directory that I wanted allow this feature within my custom vhost . To my surprise after I made the Apache config update, directory listing was not working and I was still getting the default CentOS Apache welcome page.

Apache error log:

[Sat Apr 26 14:42:11 2014] [error] [client 192.168.100.1] Directory index forbidden by Options directive: /www/mysecureshit/

It turns out the default /etc/httpd/conf.d/welcome.conf file option overrides the +Indexing Options that I explicity enabled within my custom vhost.

#
# This configuration file enables the default "Welcome"
# page if there is no default index page present for
# the root URL.  To disable the Welcome page, comment
# out all the lines below.
#

    Options -Indexes
    ErrorDocument 403 /error/noindex.html

The fix was to simply disable welcome.conf.

Linux: 

Awesome Applications: 

Pages

Premium Drupal Themes by Adaptivethemes