Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added HAProxy cookbook #54

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions cookbook/recipe-haproxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

FROM ruby:3.2-alpine

# This are the defaults if not overruled by --env VAR="" from docker run
ENV HAPROXY_GW_PORTS="2525"
ENV HAPROXY_GW_HOSTS="*"
ENV HAPROXY_GW_MAX_PROCESSINGS="4"

# Do not show extended debug log
ENV HAPROXY_GW_DEBUG="debug"

######################################################

RUN apk update
RUN apk upgrade --force
RUN apk add alpine-sdk
RUN apk add git
RUN apk add openssl-dev

ADD ./src/ /app/

WORKDIR /app

RUN ruby -v
RUN gem update --system | head -n 8
RUN bundle config set --local path '.ruby/gems'
RUN export GEMRC="Gemrc.yml"
RUN bundle install --jobs 32 --retry 3

RUN chown -R root:root /app

# Spin up service
CMD ["bundle", "exec", "ruby", "service/midi-smtp-server-recipe-haproxy-mta.rb"]
54 changes: 54 additions & 0 deletions cookbook/recipe-haproxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<p align="center" style="margin-bottom: 2em">
<img src="https://cdn.haproxy.com/assets/our_logos/haproxy-icon.svg" alt="HAProxy Logo" width="40%"/>
</p>

<br>

## HAProxy Cookbook

This is a cookbook recipe that makes use of [midi-smtp-server](https://github.com/4commerce-technologies-AG/midi-smtp-server) and
[HAProxy](http://www.haproxy.org/) to load-balance incoming connections across multiple smtp backends.
In addition there is a [Docker](https://www.docker.io/) template to build and run a full service.

Note: `haproxy` instances that are proxying smtp connections from upstream proxies, should be configured with
`accept-proxy` directive. take a look at the master and chain configurations in the [data](./data) folder.
### Settings

##### PROXY_GW_PORTS="2525"

The port(s) the mail gateway will listen on.

##### PROXY_GW_HOSTS="127.0.0.1"

The ip-address(es) the mail gateway will listen on.

##### PROXY_GW_MAX_PROCESSINGS="4"

The number of simultaneous processed connections.

##### PROXY_GW_DEBUG="debug"

Show some extended debug information.

<br>

### Usage with Docker Compose

Bring up the `haproxy-mater`, `haproxy-chain` and `smtp` services with:
```sh
docker compose up --build
```

Send test email using `haproxy-master`:
```sh
curl -v --url 'smtp://localhost:5555' -k --mail-from [email protected] --mail-rcpt [email protected] -F '='
```

Send test email directly to `smtp` service:
```sh
curl -v --url 'smtp://localhost:2525' -k --mail-from [email protected] --mail-rcpt [email protected] -F '='
```

### Author & Credits

Author: [Iuri G.](https://github.com/iuri-gg)
20 changes: 20 additions & 0 deletions cookbook/recipe-haproxy/data/proxy-chain/haproxy.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
global
log stdout local0 info
maxconn 4000

defaults
log global
mode tcp
option tcplog
option dontlognull
retries 3
timeout client 5s
timeout server 5s

frontend smtp_ft
bind :4444 accept-proxy
default_backend smtp_bk

backend smtp_bk
timeout connect 5s
server my-smtp midi-smtp:2525 send-proxy
20 changes: 20 additions & 0 deletions cookbook/recipe-haproxy/data/proxy-master/haproxy.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
global
log stdout local0 info
maxconn 4000

defaults
log global
mode tcp
option tcplog
option dontlognull
retries 3
timeout client 5s
timeout server 5s

frontend smtp_chain_ft
bind :5555
default_backend smtp_chain_bk

backend smtp_chain_bk
timeout connect 5s
server chain-smtp proxy-chain:4444 send-proxy
22 changes: 22 additions & 0 deletions cookbook/recipe-haproxy/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
services:
proxy-master:
image: haproxy:lts
user: root
volumes:
- ./data/proxy-master:/usr/local/etc/haproxy:ro
ports:
- "5555:5555"
proxy-chain:
image: haproxy:lts
user: root
volumes:
- ./data/proxy-chain:/usr/local/etc/haproxy:ro
ports:
- "4444:4444"
midi-smtp:
build:
context: ./
dockerfile: ./Dockerfile
ports:
- "2525:2525"
5 changes: 5 additions & 0 deletions cookbook/recipe-haproxy/src/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gem 'midi-smtp-server', '>=3.2.1'
20 changes: 20 additions & 0 deletions cookbook/recipe-haproxy/src/Gemrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
# sometimes update from remotes on docker will hang on IPv6
ipv4_fallback_enabled: true

# Self explanatory :-)
benchmark: false

# Verbosity of the gem command. false, true, and :really are the levels
verbose: false

# Enable/disable automatic updating of repository metadata
update_sources: true

# Print backtrace when RubyGems encounters an error
backtrace: true

# <gem_command>: A string containing arguments for the specified gem command
# -N, --no-document - Disable documentation generation
# --[no-]suggestions - Suggest alternates when gems are not found
gem: --no-document --no-suggestions
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

require 'midi-smtp-server'

# Server class
class MySmtpGw < MidiSmtpServer::Smtpd

# get each message after DATA <message> .
def on_message_data_event(ctx)
# Output for debug
logger.debug("mail received at: [#{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}], proxy #{ctx[:server][:proxy] || 'none'}, from: [#{ctx[:envelope][:from]}] for recipient(s): [#{ctx[:envelope][:to]}]...")
end

end

# flush the log output immediately after each write operation
$stdout.sync = true

# Create a new server instance for listening
# If no ENV settings use default interfaces 127.0.0.1:2525
# Attention: 127.0.0.1 is not accessible in Docker container even when ports are exposed
server = MySmtpGw.new(
ports: ENV.fetch('HAPROXY_GW_PORTS', MidiSmtpServer::DEFAULT_SMTPD_PORT),
hosts: ENV.fetch('HAPROXY_GW_HOSTS', MidiSmtpServer::DEFAULT_SMTPD_HOST),
max_processings: ENV.fetch('HAPROXY_GW_MAX_PROCESSINGS', '').to_s.empty? ? MidiSmtpServer::DEFAULT_SMTPD_MAX_PROCESSINGS : ENV.fetch('HAPROXY_GW_MAX_PROCESSINGS').to_i,
proxy_extension: true,
auth_mode: :AUTH_OPTIONAL,
tls_mode: :TLS_FORBIDDEN,
logger_severity: ENV.fetch('HAPROXY_GW_DEBUG', '').to_s.empty? ? Logger::INFO : Logger::DEBUG
)

# save flag for Ctrl-C pressed
flag_status_ctrl_c_pressed = false

# try to gracefully shutdown on Ctrl-C
trap('INT') do
# print an empty line right after ^C
puts
# notify flag about Ctrl-C was pressed
flag_status_ctrl_c_pressed = true
# signal exit to app
exit 0
end

# Output for debug
server.logger.info("Starting MySmtpGw [#{MidiSmtpServer::VERSION::STRING}|#{MidiSmtpServer::VERSION::DATE}] ...")

# setup exit code
at_exit do
# check to shutdown connection
if server
# Output for debug
server.logger.info('Ctrl-C interrupted, exit now...') if flag_status_ctrl_c_pressed
# info about shutdown
server.logger.info('Shutdown MySmtpGw...')
# stop all threads and connections gracefully
server.stop
end
# Output for debug
server.logger.info('MySmtpGw down!')
end

# Start the server
server.start

# Run on server forever
server.join
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ nav:
- Cookbook:
- Recipes: cookbook.md
- Slack MTA: cookbook_recipe_slack_mta.md
- HAProxy load-balancing: cookbook_recipe_haproxy_mta.md
- Support: support.md
- Appendix:
- Security: appendix_security.md
Expand Down
14 changes: 14 additions & 0 deletions mkdocs/cookbook_recipe_haproxy_mta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<h2>HAProxy load-balancing</h2>

HAProxy load-balancing recipe shows how to use one or more HAProxy instances to load-balance incoming connections
across multiple smtp backends.

The recipe comes with simple SMTP server that can be used as a backend for HAProxy. The server is based
on `midi-smtp-server`
and prints the proxy and sender information to the console.

Take a look at the example recipe in
the [cookbook/recipe-haproxy](https://github.com/4commerce-technologies-AG/midi-smtp-server/tree/master/cookbook/recipe-haproxy)
folder. Have fun to use and extend this recipe for your own ideas and needs.

<br>