Using NginX Route to Plex Server Behind Firewall.

Description

If you have a router and want to limit access from the outside world to all the computers inside your home, then you will probably want to set up a server in a DMZ or forward specific ports to it. This open server can then route traffic to anything behind the firewall for you — acting as a proxy. I have used both Apache and Nginx, I like both, but In this article I will show an example using Nginx. Nginx has been proven to be able to handle more concurrent requests than apache and it is very lightweight.

Directions

There are lots of tutorials on the internet that show how to install and configure nginx. The general idea is when you install nginx there are to folders (usually in /etc/nginx/) called sites-available and sites-enabled. Good practice is to make a conf file inside the sites-available directory. Then execute the following command:

nginx -t

Once the configuration file is syntactically correct you can do a symbolic link to the same filename in sites-enabled. The following configuration shows one of my configuration files that routes request to plex.domainname.com to my plex server behind the firewall. If own a domain name then you need to edit the zone file and make an A record that points to the IP address of the router. When you then request plex..com the traffic will hit your router, go through the firewall to the server, and be proxied to another location via nginx.




NGinX Server Diagram

plex.conf
[enlighter language=”json”]
upstream plex-upstream {
# change plex-server.example.com:32400 to the hostname:port of your plex server.
# this can be “localhost:32400”, for instance, if Plex is running on the same server as nginx.
server :32400;
}

server {
listen 80;

# server names for this server.
# any requests that come in that match any these names will use the proxy.
server_name plex..com;

# this is where everything cool happens (you probably don’t need to change anything here):
location / {
# if a request to / comes in, 301 redirect to the main plex page.
# but only if it doesn’t contain the X-Plex-Device-Name header
# this fixes a bug where you get permission issues when accessing the web dashboard
if ($http_x_plex_device_name = ”) {
rewrite ^/$ http://$http_host/web/index.html;
}

# set some headers and proxy stuff.
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;

# include Host header
proxy_set_header Host $http_host;

# proxy request to plex server
proxy_pass http://plex-upstream;
}
}
[/enlighter]




Node.js Webapp In a Docker Image – MEAN

Description

Node js applications are great for proof of concepts and getting apps made in a short amount of time. Running them inside a docker container is a great security blanket and lets applications be versioned much like software version control. This is a quick code snippet of a nodejs server using express. This is then packaged into a docker image that can be deployed to any machine running docker.



package.json

package.json

[enlighter language=”json”]
{
“name”: “apiTest”,
“version”: “1.0.0”,
“description”: “API for testurl.com”,
“main”: “server.js”,
“private”: “true”,
“scripts”: {
“start”: “node server.js”
},
“author”: “Derek Carr”,
“license”: “MIT”,
“url”: “http://testurl.com”,
“dependencies”: {
“body-parser”: “*”,
“express”: “^4.13.3”,
}
}
[/enlighter]

Express JS

server.js

var express = require('express');
var bodyParser = require('body-parser');

// Server Vars
var app = express();
var port = process.env.PORT || 9800;

//Configuration of App
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
// Add headers
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
app.set('trust proxy'); // trust first proxy

//TEST
app.get("/test", function (req, res, next) {
res.end("FOO TEST COMPLETE");
});

app.listen(port, function () {
console.log("Server started @ " + port);
});

module.exports = app;

Dockerfile

One of the benefits of Docker Images is the layers we can make. Most tutorials on the internet show that you need to pull down an ubuntu or centos image, install node, and do all sorts of updates to the machine in order to run npm apps. Google has a repo in hub.docker.com where they took the time to build a base image and strip it down to the bare essentials to run node apps. This makes your total image size smaller and all you have to do as a developer is make sure your package.json has the right dependencies.

Dockerfile

FROM google/nodejs-runtime

MAINTAINER Derek Carr <zaphinath@gmail.com>

WORKDIR /app
ADD package.json /app/
RUN npm install
ADD . /app

EXPOSE 9800

CMD []
ENTRYPOINT ["/nodejs/bin/npm", "start"]

Building The Image

This lets

DOCKER_IMAGE="myuser/test"
TAG="latest"
docker build -t $DOCKER_IMAGE:$TAG .
docker push $DOCKER_IMAGE:$TAG
echo "DETL_DOCKER_IMAGE=$DOCKER_IMAGE:$TAG" > version.properties
#Clean up docker images
docker images | grep "$DOCKER_IMAGE" | awk '{print $3}' | xargs docker rmi

Note

You need to have docker installed and the daemon running. You also need to be logged into docker – which can be done by running docker login from the command line.

Organizing workbench tools

Overview

I have been accumulating a few tools over the years and now is the time to start organizing them. I didn’t like the idea of loose hooks or not knowing where the tools belong, however, with some cheap black paper and locking hooks I have a solution.
IMG_1631

Supplies

  • Two 2×4 white pegboards ($6-8)
  • Three sets of 32-Piece Locking Peg Hook Assortments
  • Spacers + screws or strips of wood for mounting the pegboard onto your wall. I just bought some discounted wood, sized it, and attached it to the wall.




Knowing where your tools belong

Installing the pegboard and the pegs is pretty easy. I know when I work on projects I end up with several tools on my bench that I will need to put away when I’m finished. I want to make sure I return the tools to their rightful spots on the wall. My solution was to buy some recycled black paper from a dollar store, trace my tools on it using my tools as a stencil, cut them out, and tape them behind the tools.

pegboard backdrop

Multiple environments through GEB

I’ve recently been experimenting with GEB, which is a selenium framework that uses the groovy language. One of first challenges I encountered was trying to switch between multiple environments, instances, subdomains, or base urls. GEB uses a config file called GebConfig.groovy that is a standard groovy configuration file. This file is read by the ConfigSlurper class. The ConfigSlurper takes in different environments in the constructer. I also wanted to be able to switch environments based on a command line overwrite – either ANT or Gradle. The following is a small example on how to setup multiple environments that can be changed by a command line override.



def user = ""
def pass = ""

environment = System.getProperty("test.env", "example1")

switch(environment) {
  case "example1":
    user = "foo1"
    pass = "foo2"
    baseurl = "example1.zaphinath.com"
    break
}

The command line override in this case would be:

gradle -Dtest.env=example1 

VGA Synchronization Driver for FPGA

Description

In order to use the VGA connection on an FPGA there needs to be a vga timer that can change the pixels as it goes across the screen. I won’t go into detail here about VGA standards, or how the horizontal and vertical refreshes work, but rather just give some example VHDL code that will refresh a screen that is 640 X 480 pixels in size. This can then be declared as an entity in other VHDL code that can generate blocks and other movements on the screen.


Ports

Port Name Direction Width Purpose
clk Input 1 Input clock (50 MHz)
rst Input 1 Asynchronous reset
HS Output 1 Low asserted horizontal sync VGA signal
VS Output 1 Low asserted vertical sync VGA signal
pixel_x Output 10 Indicates the column of the current VGA pixel
pixel_y Output 10 Indicates the row of the current VGA pixel
last_column Output 1 Indicates that the current pixel_x correspond to the last visible column
last_row Output 1 Indicates that the current pixel_y corresponds to the last visible row
blank Output 1 Indicates that the current pixel is part of a horizontal or vertical retrace and that the output color must be blanked. The VGA pixel must be set to “Black” during blanking.

Screen Shot 2013-12-24 at 10.56.10 PM

VHDL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

entity vga_timing is
    Port ( clk : in  STD_LOGIC;
           rst : in  STD_LOGIC;
           HS : out  STD_LOGIC;
           VS : out  STD_LOGIC;
           pixel_x : out  STD_LOGIC_VECTOR (9 downto 0);
           pixel_y : out  STD_LOGIC_VECTOR (9 downto 0);
           last_column : out  STD_LOGIC;
           last_row : out  STD_LOGIC;
           blank : out  STD_LOGIC);
end vga_timing;

architecture vga_timer of vga_timing is

signal pixel_en, row_en, visible, column_intermed: STD_LOGIC;
signal horiz_counter, vertical_counter, nexthcount, nextvcount : unsigned (9 downto 0) := (others=>'0');

begin

--toggle register for pixel en, giving 25MHz clk
process(clk,rst)
begin
if (rst = '1') then 
pixel_en <= '0';
elsif (clk'event and clk = '1') then
pixel_en <= not pixel_en;
end if;
end process;

--horizontal pixel counter register based off of pixel_en
process(clk,pixel_en,rst)
begin
if (rst = '1') then
horiz_counter <= (others => '0');
elsif (clk'event and clk = '1') then
        if (pixel_en = '1') then
        horiz_counter <= nexthcount;
        end if;
end if;
end process;

nexthcount <= (horiz_counter + 1) when (horiz_counter < 799) else
                                        (others => '0');
                                        
pixel_x <= std_logic_vector(horiz_counter);
column_intermed <= '1' when (horiz_counter = 639)
                                        else '0';
row_en <= '1' when (horiz_counter = 799) else 
                         '0';
last_column <= column_intermed;
                                        
HS <= '0' when ((horiz_counter > 655) and (horiz_counter < 752))
                else '1';
                
--vertical pixel counter increments only with last_column
process(clk,rst,row_en)
begin
if (rst = '1') then
vertical_counter <= (others => '0');

Connecting to an API WSDL through Java REST

Description

Java seems to have a pretty decent library for dealing with WSDL soap transactions, however there doesn’t seem to be any great library out there for dealing with rest. The solution is to using java’s built httpconnection library to post the information to the appropriate url and wait for a response. For REST protocol we will first encode a JSON array that contains the proper commands and parameters according the WSDL API you are trying to interface with. Next the JSON encoded array is posted to a url, which should return a response that can be processed.

Encoding in JSON

The first thing to do is look at the API’s web service description language to see the format of the JSON encoded array should be made. The following is an example from the InsideSales.com WSDL. This method call is meant to delete a lead from the system.
deleteLead

public int deleteLead(int leadId, boolean purge) {
		int returnLeadId = 0;
		JSONObject json = new JSONObject();
		JSONArray list = new JSONArray();
		try {
			list.put(leadId);
			list.put(purge);
			json.put("operation", "deleteLead");
			json.put("parameters", list);
		} catch (JSONException e) {
			e.printStackTrace();
		}
		String response = wsm.sendJson(json);
		returnLeadId = Integer.valueOf(response);
		return returnLeadId;
	}

The method wsm.sendJson(json) is in the next section. I made a wrapper class to handle various api connections and responses. I put these all in a class I called WebServiceManager.

HTML Post

Once we have the JSON encoded array we need to post it to the appropriate URL. Any WSDL documentation should give the url that the JSON object should be posted to. This means we need to have a function that will allow us to use Java to make the post.

	public String sendJson(JSONObject json) {
		HttpResponse response = null;
		httpClient = HttpClientBuilder.create().build();
		StringBuilder sb = new StringBuilder();
		try {
			request = new HttpPost(destination);
			StringEntity params = new StringEntity(json.toString());
			request.addHeader("content-type", "application/x-www-form-urlencoded");
			request.addHeader("Cookie", this.cookies);
			request.setEntity(params);
			response =  httpClient.execute(request);
			InputStream ips  = response.getEntity().getContent();
	        BufferedReader buf = new BufferedReader(new InputStreamReader(ips,"UTF-8"));
	        if(response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
	            throw new Exception(response.getStatusLine().getReasonPhrase());
	        }
	        String s;
	        while((s = buf.readLine()) != null) {
	            sb.append(s);
	        }
	        buf.close();
	        ips.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return sb.toString();
	}

http map

The image to the right shows the basic idea of how the hypertext transfer protocol works with a post request. Almost all API’s that exist will require some type of authentication, which I don’t show here, but you do some type of a login method and the server will send back a session id or some type of a cookie that you can store and use for subsequent methods so you don’t need to authenticate with every API request. This is why we send the cookie that we got from authentication with every request. The actual JSON encoded array of commands and parameters goes in the entity body of the post. After the server responds saying that the request is good we can read the response from the server.


Decoding JSON

In the previous section we send an http post request, and then read the response stream. The method deleteLead in this case returns the id of the lead that was deleted, so I just parse the string into an integer and handle it as I need. Other methods such as getLead will return a whole lead object, and in this section the JSON encoded response array will need to be decoded and put into a lead object.

String response = wsm.sendJson(json);
returnLeadId = Integer.valueOf(response);
return returnLeadId;

Project Organization

I have put this off for too long now – I am finally collecting all my projects, both personal and school related, and putting them up on github. Zaphinath’s GitHub repository. I have been using SVN and git for sometime on my own boxes. There are things I like about both and things I dislike. SVN has better version control and reverting abilities, but git is better to fork and merge code. Check out the repository sometime!

PS2 Receiver Module for VHDL

Description

I have several VHDL modules that have been coded to use with the Spartan 3E. My next couple of posts will have various modules followed by a top level design that connects them all together. This module is for a keyboard receiver. Keyboards communicate two ways. They transmit the key strokes in a 11 bit serial transmission. The first bit is a start bit, followed by 8 bits of data, then a parody bit, and finally ends with the stop bit.

Each key is transmitted one at a time. It is up the bios to interpet if keys are pushed in a combination, if a shift key is pushed, or if caps lock is on. Fortunately the ps2 protocol allows an easy way to check if shift is being pushed or not.

The key is pushed and transmits the keyboard code, and then periodically retransmits that code if the key is held down. When the key is released the keyboard will translate the hex value of F0 and then transmits the pressed key again. I use this to set a register if the shift is pushed and when the code gets resent it sets the register to to zero. The state machine for the ps2 receiver then just disregards two data sets sent after F0.

PS2 Rx


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity ps2_rx is 
	port (
		clk, rst : in std_logic;
		ps2d, ps2c : in std_logic;
		rx_en : in std_logic;
		rx_done_tick : out std_logic;
		shift_en : out std_logic;
		dout : out std_logic_vector(7 downto 0)
	);
end ps2_rx;

architecture arch of ps2_rx is
	type statetype is (idle, dps, load);
	signal state_reg, state_next : statetype;
	-- filter
	signal filter_reg, filter_next : std_logic_vector(7 downto 0);
	signal f_ps2c_reg, f_ps2c_next : std_logic;

	signal b_reg, b_next : std_logic_vector(10 downto 0);
	signal n_reg, n_next : unsigned(3 downto 0);
	signal shift_reg, shift_next : std_logic := '0';
	signal last_reg, last_next : std_logic := '0';
	
	signal fall_edge : std_logic;
	
begin

-- filter 
process (clk, rst) 
begin
  if (rst = '1') then
    filter_reg <= (others => '0');
    f_ps2c_reg <= '0';
  elsif (clk'event and clk='1') then
    filter_reg <= filter_next;
    f_ps2c_reg <= f_ps2c_next;
  end if;
end process;


filter_next <= ps2c & filter_reg(7 downto 1);
f_ps2c_next <= '1' when filter_reg = "11111111" else
               '0' when filter_reg = "00000000" else
               f_ps2c_reg;

fall_edge <= f_ps2c_reg and (not f_ps2c_next);

-- registers
process (clk, rst)
begin
	if (rst = '1') then
		state_reg <= idle;
		n_reg <= (others => '0');
		b_reg <= (others => '0');
	elsif (clk'event and clk='1') then
		state_reg <= state_next;
		n_reg <= n_next;
		b_reg <= b_next;
		shift_reg <= shift_next;
		last_reg <= last_next;
	end if;
end process;

-- next-state logic
process(state_reg, n_reg, b_reg, fall_edge, rx_en, ps2d, shift_reg, last_reg)
begin
	rx_done_tick <= '0';
	state_next <= state_reg;
	n_next <= n_reg;
	b_next <= b_reg;
	shift_next <= shift_reg;
	last_next <= last_reg;
  
  case state_reg is 
    when idle =>
      if (fall_edge = '1' and rx_en='1') then 
        --shift in start bit
        b_next <= ps2d & b_reg(10 downto 1);
        n_next <= "1001"; -- set count to 8 again
        state_next <= dps;
      end if;
    when dps => 
      if (fall_edge = '1' ) then
        b_next <= ps2d & b_reg(10 downto 1);
        if (n_reg = 0) then 
          state_next <= load;
        else 
          n_next <= n_reg - 1;
        end if;
      end if;
    when load =>
		-- here we handle if signal f0 and following signal are 
		-- asserted - we don't want to transmit them to dout.
      -- one more state to complete last shift
		state_next <= idle;
		rx_done_tick <= '1';
		if (b_reg(8 downto 1) = x"12" or b_reg(8 downto 1) = x"59") then
			shift_next <= not shift_reg;
			if (last_reg = '1') then
				rx_done_tick <= '1';
				last_next <= '0';
			end if;
		elsif (b_reg(8 downto 1) = "11110000") then
			last_next <= '1';
			rx_done_tick <= '0';
		elsif (last_reg = '1') then
			last_next <= '0';	
			rx_done_tick <= '0';
		end if;
  end case;
end process;

shift_en <= shift_reg;
dout <= b_reg(8 downto 1);

end arch;

Keyboard to ASCII Decoder

There are 2^8 combinations – 256 options available and each keyboard code needs to be decoded to ascii codes. The following is a decoder checks if the registers are set for either caps or shift and determines the ascii output based on keyboard inputs.

key2ascii

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity key2ascii is
	port (
		caps_enabled : in std_logic;
		shift_enabled : in std_logic;
		key_code : in std_logic_vector(7 downto 0);
		ascii_code : out std_logic_vector(7 downto 0)
	);
end key2ascii;	

architecture arch of key2ascii is 
signal ascii_normal, ascii_caps : std_logic_vector(7 downto 0);
begin
	with key_code select
		ascii_normal <=
			"00110000" when "01000101", --0
			"00110001" when "00010110", --1
			"00110010" when "00011110", --2
			"00110011" when "00100110", --3
			"00110100" when "00100101", --4
			"00110101" when "00101110", --5
			"00110110" when "00110110", --6
			"00110111" when "00111101", --7
			"00111000" when "00111110", --8
			"00111001" when "01000110", --9
			
			"01100001" when "00011100", --a
			"01100010" when "00110010", --b
			"01100011" when "00100001", --c
			"01100100" when "00100011", --d
			"01100101" when "00100100", --e
			"01100110" when "00101011", --f
			"01100111" when "00110100", --g
			"01101000" when "00110011", --h
			"01101001" when "01000011", --i
			"01101010" when "00111011", --j
			"01101011" when "01000010", --k
			"01101100" when "01001011", --l
			"01101101" when "00111010", --m
			"01101110" when "00110001", --n
			"01101111" when "01000100", --o
			"01110000" when "01001101", --p
			"01110001" when "00010101", --q
			"01110010" when "00101101", --r
			"01110011" when "00011011", --s
			"01110100" when "00101100", --t
			"01110101" when "00111100", --u
			"01110110" when "00101010", --v
			"01110111" when "00011101", --w
			"01111000" when "00100010", --x
			"01111001" when "00110101", --y
			"01111010" when "00011010", --z
			
			"01100000" when "00001110", --'
			"00101101" when "01001110", ---
			"00111101" when "01010101", --=
			"01011011" when "01010100", --[
			"01011101" when "01011011", --]
			"01011100" when "01011101", --
			"00111011" when "01001100", --;
			"00100111" when "01010010", --
			"00101100" when "01000001", --,
			"00101110" when "01001001", --.
			"00101111" when "01001010", --/
			"01111010" when "11110000", -- F0
			
			"00100000" when "00101001", -- space
			"00001101" when "01011010", -- enter
			"00001000" when "01100110", -- backspace
			"00101010" when others; --*			
	
	with key_code select
		ascii_caps <=
			"00101001" when "01000101", --)
			"00010001" when "00010110", --!
			"00100000" when "00011110", --@
			"00010011" when "00100110", --#
			"00010100" when "00100101", --$
			"00100101" when "00101110", --%
			"01011110" when "00110110", --^
			"00100110" when "00111101", --&
			"00101010" when "00111110", --*
			"00101000" when "01000110", --(
			
			"01000001" when "00011100", --A
			"01000010" when "00110010", --B
			"01000011" when "00100001", --C
			"01000100" when "00100011", --D
			"01000101" when "00100100", --E
			"01000110" when "00101011", --F
			"01000111" when "00110100", --G
			"01001000" when "00110011", --H
			"01001001" when "01000011", --I
			"01001010" when "00111011", --J
			"01001011" when "01000010", --K
			"01001100" when "01001011", --L
			"01001101" when "00111010", --M
			"01001110" when "00110001", --N
			"01001111" when "01000100", --O
			"01010000" when "01001101", --P
			"01010001" when "00010101", --Q
			"01010010" when "00101101", --R
			"01010011" when "00011011", --S
			"01010100" when "00101100", --T
			"01010101" when "00111100", --U
			"01010110" when "00101010", --V
			"01010111" when "00011101", --W
			"01011000" when "00100010", --X
			"01011001" when "00110101", --Y
			"01011010" when "00011010", --Z
			
			"00111111" when "01001010", --?
			
			"00100000" when "00101001", -- space
			"00001101" when "01011010", -- enter
			"00001000" when "01100110", -- backspace
			"00101010" when others; --*  
		
	ascii_code <= ascii_normal when (caps_enabled = '0' and shift_enabled = '0') else
					  ascii_caps when (caps_enabled = '0' and shift_enabled = '1') else
--					  ascii_caps when (caps_enabled = '1' and shift_enabled = '0') else
					  ascii_normal;
end arch;

Using Asterisk as Sip Server with Google Voice

Description

I have wanted for some time to buy a sip phone to plug directly into my network and use it with my google voice account, thus giving me a free house phone with unlimited minutes to all of north america (or wherever google decides). To do this I installed Asterisk 11.0 on my server and followed the documentation to get this set up. There were a few caveats that I will share, but the following link has everything needed to get asterisk working with your google voice phone number.



https://wiki.asterisk.org/wiki/display/AST/Calling+using+Google

Using Asterisk with your google voice account is easy, but having a basic understanding of what is needed is absolutely essential. There are two protocols that Asterisk uses to control and send voice information with your phone, with either soft or hard phones. The SIP protocol controls the functions of your call – such as dialing, hanging up, transferring, and etc. The actual conversation for the phone is transported over RTP. Each one of these protocols uses UDP rather than TCP, and they both use different ports. In the sip.conf file you specify which port you want SIP to use, the default is 5060. In the rtp.conf file you need to actually specify a range of UDP ports that the protocol can use. The smaller the number of this range the more secure you will be, but don’t make it too small that you have no available ports to use if hanging up does not work successfully.

The other vital thing to understand is you are not connecting directly to google voice’s sip server. Rather you are connecting through jabber or xmpp. Connecting to the chat server will then allow you to handle call control.

Caveats

  • The first problem I ran into is a fresh install of Asterisk includes far more configuration information than you actually need. In order for the SIP and RTP to work well I ended up commenting out almost all of the rtp.conf and sip.conf files in /etc/asterisk/.
  • The second problem I had was with the dial plan in the extensions.conf. I wanted to be able to dial long distance numbers without dialing 1 in front of the number. So I added the following outbound dial plan to use.
    [out]
    exten => 100,1,Dial(Motif/google/zaphinath@gmail.com,,r)
    exten => _1XXXXXXXXXX,1,Dial(Motif/google/${EXTEN}@voice.google.com,,r)
    exten => _XXXXXXXXXX,1,Dial(Motif/google/1${EXTEN}@voice.google.com,,r)
    

    Then in the sip.conf I set the context=out for my user.

  • The last major problem I had was my firewall. I needed to open the UDP ports on my firewall. I use iptables for my firewall. I opened the ports successfully, but every time I used netstat or nmap to check if the port was open I wasn’t seeing anything. Later I discovered that UDP ports don’t actively listen like TCP ports and the ports were indeed open even if a scan wasn’t always showing it.

My next step is to set up better IP filters and error messaging so I can use Iptables to deny erroneous requests.

Custom Filter for Exim Through Fail2ban

Description

I have noticed a lot of unsolicited smtp traffic on my box and I have started taking anti-spam measures to remove unauthorized usage of mail on my server. I use exim to transport and route mail. Exim is great and there are a lot of custom tweaks that can be done with Exim’s ACLs to prevent spam. However I feel there is even another step that can be taken to prevent the waste of precious cpu resources to fight spam. I love iptables and an extension to iptables – fail2ban.

Procudure

  1. Install and use exim and fail2ban
  2. Edit /etc/fail2ban/jail.conf and add the following section:
    [exim]
    enabled = true
    filter  = exim
    port    = smtp,ssmtp
    action  = iptables-allports[name=exim, protocol=tcp]
    #action   = iptables[name=exim, port="smtp", protocol=tcp]
    logpath = /var/log/exim/reject.log
    maxretry = 1
    
  3. Edit /etc/fail2ban/filter.d/exim.conf
    # Fail2Ban configuration file
    #
    # Author: Derek Carr
    
    [Definition]
    failregex = .*[<HOST>].*[192.168.0.196].*(?:rejected by local_scan|Unrouteable (address|user)|rejected RCPT.*(callout verification failure|response.*Unknown user|relay not permitted|)|Host is listed in*|See RFC821)
    
    ignoreregex = 
    

    NOTE: This filter is based off of python’s regex library and I am filtering this based of how my exim.conf sends denied requests to the log file. A great way to test either my configuration or custom regular expressions you use to match your reject.log is the following:

    fail2ban-regex /var/log/exim/reject.log ".*[<HOST>].*[192.168.0.196].*(?:rejected by local_scan|Unrouteable (address|user)|rejected RCPT.*(callout verification failure|response.*Unknown user|relay not permitted|)|Host is listed in*|See RFC821)"
    

    192.168.0.196 is my local mail relay, you might want to include your LAN ip address of your box. This really depends on how the rejected addresses look like inside your exim/reject.log

  4. Restart fail2ban
    /etc/init.d/fail2ban restart
    

Conclusion

The great thing about this configuration is exim is blocking spam emails based off access control lists, checking against spamhaus and other blacklist providers, and etc, but we can save our computer the cpu cycles needed to check through all of exim’s configuration every time. Instead we take the list of repeat offenders and block them inside iptables using fail2ban. This way the next time they try to connect to the computer they are blocked before they even make it to exim. To view currently blocked IP’s trying to use your mail server just use the following:

# To view the currently blocked IP's
iptables -nL | grep -A100000 'Chain fail2ban-exim'
# To count how many accounts are banned currently
iptables -nL | grep -A100000 'Chain fail2ban-exim' | wc -l