Blackhole Documentation

Blackhole Documentation
Release 1.6.0
Kura
April 30, 2013
CONTENTS
1
Using the blackhole.io service
1.1 Testing via telnet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Testing SSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3
3
3
2
Getting the source code
5
3
Running your own server
3.1 Python versions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Third party libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
7
7
4
Tests & Coverage
4.1 Running tests manually . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 Third party CI/Coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
13
13
13
5
Blackhole + PyPy
15
6
FAQ
17
7
Reference
7.1 blackhole.connection
7.2 blackhole.data . . . . .
7.3 blackhole.log . . . . . .
7.4 blackhole.opts . . . . .
7.5 blackhole.ssl_utils .
7.6 blackhole.state . . . .
7.7 blackhole.utils . . . .
8
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19
19
19
20
20
20
21
21
Indices and tables
23
Python Module Index
25
i
ii
Blackhole Documentation, Release 1.6.0
.o8
oooo
"888
‘888
888oooo.
888
.oooo.
d88’ ‘88b 888 ‘P )88b
888
888 888
.oP"888
888
888 888 d8( 888
‘Y8bod8P’ o888o ‘Y888""8o
oooo
oooo
‘888
‘888
.ooooo.
888 oooo
888 .oo.
d88’ ‘"Y8 888 .8P’
888P"Y88b
888
888888.
888
888
888
.o8 888 ‘88b.
888
888
‘Y8bod8P’ o888o o888o o888o o888o
.ooooo.
d88’ ‘88b
888
888
888
888
‘Y8bod8P’
oooo
‘888
888
888
888
888
o888o
o8o
‘"’
.ooooo.
oooo
d88’ ‘88b
‘888
888ooo888
888
888
.o .o. 888
‘Y8bod8P’ Y8P o888o
Blackhole is a Tornado powered MTA (mail transport agent) that is designed for handling large volumes of email
without handling any of the messages and doing no disk bound I/O.
Blackhole is designed mostly for testing purposes and can be used to test numerous things suchs as;
• Email send rates, if you need to test how much mail you can send per minute, hour etc
• Email integration testing and finally
• if you work in the real world, chances are you’ll need work on a copy of production data from time to time. You
can try to anonymous all the data but there is always a chance you’ll miss something. Configuring blackhole
as your applications default SMTP gateway will remove any chance of a real person receiving an email they
shouldn’t have received.
CONTENTS
1
.oo
d88’
888
888
‘Y8b
Blackhole Documentation, Release 1.6.0
2
CONTENTS
CHAPTER
ONE
USING THE BLACKHOLE.IO SERVICE
All data sent to blackhole.io will be forgotten instantly, we store nothing you send.
1. Point your application’s outgoing SMTP server to ‘blackhole.io’,
2. Sit back and watch mail never get delivered to a real user.
or, send an email to blackhole.io using an @blackhole.io address, any address is fine e.g.:
[email protected]
1.1 Testing via telnet
$ telnet blackhole 25
Trying 198.199.126.159...
Connected to blackhole.io.
Escape character is ’^]’.
220 2.2.0 OK, ready
HELO fake.mail.server
250 2.5.0 OK, done
MAIL FROM:<[email protected]>
250 2.5.0 OK, done
RCPT TO:<[email protected]>
250 2.5.0 OK, done
DATA
354 3.5.4 Start mail input; end with <CRLF>.<CRLF>
To: Someone <[email protected]>
From: User <[email protected]>
Subject: Bye
Bye bye email
.
251 2.5.1 OK, user not local, will forward
QUIT
221 2.2.1 Thank you for speaking to me
Connection closed by foreign host.
1.2 Testing SSL
3
Blackhole Documentation, Release 1.6.0
$ openssl s_client -connect blackhole.io:465
CONNECTED(00000003)
depth=0 C = GB, ST = London, L = London, O = blackhole.io, OU = blackhole.io, CN = blackhole.io, emai
... snip ...
--220 2.2.0 OK, ready
HELO fake.mail.server
250 2.5.0 OK, done
MAIL FROM:<[email protected]>
250 2.5.0 OK, done
RCPT TO:<[email protected]>
250 2.5.0 OK, done
DATA
354 3.5.4 Start mail input; end with <CRLF>.<CRLF>
To: Someone <[email protected]>
From: User <[email protected]>
Subject: Bye
Bye bye email
.
251 2.5.1 OK, user not local, will forward
QUIT
221 2.2.1 Thank you for speaking to me
DONE
4
Chapter 1. Using the blackhole.io service
CHAPTER
TWO
GETTING THE SOURCE CODE
The source code is available under the MIT license from GitHub.
5
Blackhole Documentation, Release 1.6.0
6
Chapter 2. Getting the source code
CHAPTER
THREE
RUNNING YOUR OWN SERVER
3.1 Python versions
Python 2.6
Python 2.7
Python 3.2
Python 3.3
PyPy 1.9
PyPy 2.0
# see notes below
# see notes below
Blackhole works on Python 2.6 and 2.7, it also works with PyPy (see Blackhole + PyPy section below).
3.2 Third party libraries
tornado>=2.2.1,<=3.1
setproctitle>=1.1.6
deiman>=0.1.4
# setproctitle 1.1.7 and above are required for all PyPy versions
# older version of Deiman will not work because of API changes
3.3 Getting started
3.3.1 Installation
Packaged
From PyPI
pip install blackhole
Or
easy_install blackhole
From GitHub
pip install -e git+git://github.com/kura/blackhole.git#egg=blackhole
7
Blackhole Documentation, Release 1.6.0
From source
Download the latest tarball from PyPI or GitHub. Unpack and run:
python setup.py install
3.3.2 Configuration options
Configuration options can be passed via the command line as below:
--workers=NUM
Number of child workers to spawn (default: # of CPUs/cores + 1
master)
--host=IP
IP address to bind go (default: 0.0.0.0)
--port=PORT
Port to listen for connections on (default: 25)
--pid=FILE
File to write process information to (default: /tmp/blackhole.pid)
--log=FILE
File to write logs to (not very verbose) (default: /tmp/blackhole.log)
--user=USER
User to drop privs to during run time. (default: blackhole)
--group=GROUP
Group to drop privs to during run time. (default: blackhole)
--mode=MODE
Mode to run blackhole in (accept, bounce, random, unavailable, offline) (default: accept) - see Modes section
--debug=BOOL
Will set the debug flag and causes all received and sent command/email data to be logged. Will degrade performance with high
throughput (default: False)
--ssl=BOOL
Enabled or disable SSL, requires SSL compiled in to Python and
OpenSSL. True or False (default: True)
--ssl_port=PORT
Port to listen for SSL enabled connections (default: 465)
--ssl_key=PATH
X509 SSL keyfile
--ssl_cert=PATH
X509 SSL certificate file
SSL options
You can also specify the –conf option to load configuration from a file:
--conf=FILE
Config file to parse and use. Overrides command line args
For more information on the configuration file format read Configuration file example.
Deprecated options
The following options are deprecated and should not be used.
--ssl_ca_certs_dir=PATH Path to your operating system’s repository of certificates authorities (default: None)
3.3.3 Configuration file example
8
Chapter 3. Running your own server
Blackhole Documentation, Release 1.6.0
host="0.0.0.0"
port=25
pid="/tmp/blackhole.io"
mode="offline"
ssl_key=/etc/ssl/private/blackhole.io.key
ssl_cert=/etc/ssl/certs/blackhole.io.crt
3.3.4 Debug Flag
After much debate and several emails from developers a debug flag has been added to blackhole.
You can enable debug using command line options Configuration options or via the configuration file Configuration
file example.
Enabling the debug flag will log all incoming and outgoing commands to the blackhole log file as well as all data sent
after a DATA command.
Each connection/email gets it’s own unique identifier, this indentifier uses only hexidecimal characters and is 10
character in length, which means at some point the identifier will be reused.
Example debug output
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
2013-04-14
07:30:30,788
07:30:31,813
07:30:31,814
07:30:35,074
07:30:35,075
07:30:38,402
07:30:38,402
07:30:40,573
07:30:40,573
07:30:47,143
07:30:52,181
07:30:54,647
07:30:56,550
07:30:57,372
07:30:58,286
07:30:58,287
07:31:04,023
07:31:04,024
07:31:04,024
-
Connection from ’127.0.0.1’
[9AF260AB7F] RECV: HELO
[9AF260AB7F] SEND: 250 2.5.0 OK, done
[9AF260AB7F] RECV: MAIL FROM:<[email protected]>
[9AF260AB7F] SEND: 250 2.5.0 OK, done
[9AF260AB7F] RECV: RCPT TO:<[email protected]>
[9AF260AB7F] SEND: 250 2.5.0 OK, done
[9AF260AB7F] RECV: DATA
[9AF260AB7F] SEND: 354 3.5.4 Start mail input; end with <CRLF>.<CRLF>
[9AF260AB7F] RECV: From: <[email protected]>
[9AF260AB7F] RECV: To: <[email protected]>
[9AF260AB7F] RECV: Subject: Testing debug
[9AF260AB7F] RECV: Hi [email protected]
[9AF260AB7F] RECV: This is a debug message
[9AF260AB7F] RECV: .
[9AF260AB7F] SEND: 250 2.5.0 OK, done
[9AF260AB7F] RECV: QUIT
[9AF260AB7F] SEND: 221 2.2.1 Thank you for speaking to me
Closing
3.3.5 Controlling the server
The following commands are for use when using blackhole without an init script.
blackhole start
blackhole stop
blackhole restart
blackhole status
Starts the server
Stops the server
Restarts the server
Shows the status of the server, running, not running etc
For more information on controlling the server using init.d/rc.d read Controlling the server with init.d/rc.d.
3.3. Getting started
9
Blackhole Documentation, Release 1.6.0
3.3.6 Controlling the server with init.d/rc.d
/etc/init.d/blackhole start
/etc/init.d/blackhole stop
/etc/init.d/blackhole restart
/etc/init.d/blackhole status
Starts the server
Stops the server
Restarts the server
Shows the status of the server, running, not running etc
Installating the init.d/rc.d scripts
The init script depends on /etc/blackhole.conf being in place and configured.
Blackhole comes with a script that works with init.d/rc.d, to install it copy it from the init.d/YOUR_DISTRO folder in
the root directory of this project to /etc/init.d/.
The init scripts can be found here.
i.e. for Debian/Ubuntu users, mv the file from init.d/debian-ubuntu/ to /etc/init.d/.
Then make sure it’s executable:
chmod +x /etc/init.d/blackhole
To make blackhole start on a reboot use the following:
update-rc.d blackhole defaults
3.3.7 Modes
The server can run in several different modes, these are outlined below.
See the Response codes section for more information on responses and which mode responds with which codes.
3.3.8 accept
Accept all email with code 250, 251, 252 or 253.
3.3.9 bounce
Bounce all email with a random code, excluding 250, 251, 252, 253.
3.3.10 random
Randomly accept or bounce all email with a random code.
3.3.11 unavailable
Server always respondes with code 421 - service is unavailable.
3.3.12 offline
Server always responds with code 521 - server does not accept mail.
10
Chapter 3. Running your own server
Blackhole Documentation, Release 1.6.0
3.3.13 Response codes
All
Code
220
221
250
251
252
253
354
355
421
450
451
452
454
458
459
500
501
502
503
504
521
530
534
538
550
551
552
553
554
571
message
OK, ready
Thank you for speaking to me
OK, done
OK, user not local, will forward
OK, cannot VRFY user but will attempt delivery
OK, messages pending
Start mail input; end with <CRLF>.<CRLF>
Octet-offset is the transaction offset
Service not available, closing transmission channel
Requested mail action not taken: mailbox unavailable
Requested action aborted: local error in processing
Requested action not taken: insufficient system storage
TLS not available due to temporary reason
Unable to queue message
Not allowed: unknown reason
Command not recognized
Syntax error, no parameters allowed
Command not implemented
Bad sequence of commands
Command parameter not implemented
Machine does not accept mail
Must issue a STARTTLS command first
Authentication mechanism is too weak
Encryption required for requested authentication mechanism
Requested action not taken: mailbox unavailable
User not local
Requested mail action aborted: exceeded storage allocation
Requested action not taken: mailbox name not allowed
Transaction failed
Blocked
Accept
This mode will respond with the following codes:
Codes
250 251
252
253
Bounce
This mode will respond with the following codes:
Codes
421 431
521 534
450
550
451
551
3.3. Getting started
452
552
454
553
458
554
459
571
11
Blackhole Documentation, Release 1.6.0
Offline
This mode will respond with the following codes:
Codes
521
Unavailable
This mode will respond with the following codes:
Codes
421
12
Chapter 3. Running your own server
CHAPTER
FOUR
TESTS & COVERAGE
4.1 Running tests manually
Running tests manually is pretty simple, there is a Make target dedicated to it.
The test suite relies on unittest2 and nose, both if which get installed by the Make target during test running.
make tests
There is also a Make target for generating coverage:
make coverage
4.2 Third party CI/Coverage
You can find the latest build status on travis.
And the test coverage report on coveralls.
13
Blackhole Documentation, Release 1.6.0
14
Chapter 4. Tests & Coverage
CHAPTER
FIVE
BLACKHOLE + PYPY
PyPy is a Python interpreter and just-in-time compiler. PyPy focuses on speed, efficiency and compatibility with the
original CPython interpreter.
Blackhole works well under PyPy 1.9, 2.0 beta1 and 2.0 beta2, you can see performance improvements of up to 30%
in certain situations.
However, blackhole does have issues with both PyPy 1.9 and 2.0 beta1 and 2.0 beta2 when using the pre-compiled
binaries, this is due to a conflict in the version of OpenSSL compiled in to PyPy and the version compiled in to your
CPython installation. If you wish to use blackhole with SSL support on PyPy I suggest you either compile PyPy
yourself or try to make sure your PyPy and CPython have the same versions.
15
Blackhole Documentation, Release 1.6.0
16
Chapter 5. Blackhole + PyPy
CHAPTER
SIX
FAQ
A few people have emailed me questions about why blackhole exists, how I use it, why Tornado and things like that
so I have outlined some questions and responses in an faq.
17
Blackhole Documentation, Release 1.6.0
18
Chapter 6. FAQ
CHAPTER
SEVEN
REFERENCE
7.1 blackhole.connection
Provide mechanisms for processing socket data.
This module provides methods for Blackhole to use internally for binding and listening on sockets as well as process
all incoming socket data and responding appropriately.
7.1.1 blackhole.connection
blackhole.connection.sockets()
Spawn a looper which loops over socket data and creates the sockets.
It should only ever loop over a maximum of two - standard (std) and SSL (ssl).
This way we’re able to detect incoming connection vectors and handle them accordingly.
A dictionary of sockets is then returned to later be added to the IOLoop.
blackhole.connection.connection_stream(connection)
Detect which socket the connection is being made on, create and iostream for the connection, wrapping it in
SSL if connected over the SSL socket.
The parameter ‘connection’ is an instance of ‘socket’ from stdlib.
blackhole.connection.handle_command(line, mail_state)
Handle each SMTP command as it’s sent to the server
The paramater ‘line’ is the currently stream of data ending in ‘n’. ‘mail_state’ is an instance of ‘blackhole.state.MailState’.
blackhole.connection.connection_ready(sock, fd, events)
Accepts the socket connections and passes them off to be handled.
‘sock’ is an instance of ‘socket’. ‘fd’ is an open file descriptor for the current connection. ‘events’ is an integer
of the number of events on the socket.
blackhole.connection.write_response(stream, mail_state, resp)
Write the response back to the stream
7.2 blackhole.data
Provides SMTP response codes and methods for returning the correct response code.
19
Blackhole Documentation, Release 1.6.0
This module contains all usable SMTP response codes for returning through the socket. It also provides mechanisms
for picking response codes that mean a mail message has been accepted, rejected or that the server is offline.
7.2.1 blackhole.data
blackhole.data.response(response)
Return an SMTP response code and message. ‘response’ an string refering to the code you wish to return.
blackhole.data.get_response()
Get our response from available responses based on configuration settings.
blackhole.data.random_choice(response_list)
Pick a random choice for the configured choices dictionary.
‘response_list’ is a list of available response types from ‘blackhole.data’, this can be: - ACCEPT_RESPONSES - BOUCNE_RESPONSES - OFFLINE_RESPONSES - UNAVAILABLE_RESPONSES
- RANDOM_RESPONSES
blackhole.data.response_message(response)
Format our response in ESMTP format.
‘response’ is a string containing the reponse code you wish to return.
7.3 blackhole.log
Logging configuration for the blackhole server.
7.4 blackhole.opts
Command line options for the blackhole server.
Also creates a list of available ports for the server to be run on, based on configuration and responds with the help
menu when requested or invalid options are given.
7.4.1 blackhole.opts
blackhole.opts.ports()
Create and return a list of sockets we need to create.
A maximum of two will be returned, default is standard (std) and SSL (ssl) it is enabled.
blackhole.opts.print_help(file)
Prints all the command line options to stdout.
7.5 blackhole.ssl_utils
Utility functions for SSL wrapped sockets.
This module provides a simple default SSL configuration for the blackhole server and also exposes a custom ‘BlackholeSSLException’ for SSL-based exceptions.
20
Chapter 7. Reference
Blackhole Documentation, Release 1.6.0
7.5.1 blackhole.ssl_utils
exception blackhole.ssl_utils.BlackholeSSLException
A simple Exception class
blackhole.ssl_utils.verify_ssl_opts()
Verify our SSL configuration variables are correctly set-up.
7.6 blackhole.state
State object for the current connection.
7.6.1 blackhole.state
class blackhole.state.MailState
A state object used for remembering the current connections place in our runtime.
This is mostly used for figuring out if we’re receiving SMTP commands or have trigger the DATA command.
email_id
Email ID is used to assign commands sent and received against an email/connection ID.
Only utilized when debug flag is set.
reading
MailState’s ‘reading’ property, used for figuring out where we are in the state chain.
7.7 blackhole.utils
A utility module used for methods and features that do not belong in their own module.
7.7.1 blackhole.utils
blackhole.utils.setgid()
Change our existing group.
Used to drop from root privileges down to a less privileged group.
MUST be called BEFORE setuid, not after.
blackhole.utils.setuid()
Change our existing user.
Used to drop from root privileges down to a less privileged user
MUST be called AFTER setgid, not before.
blackhole.utils.terminate(signum, frame)
Terminate the parent process and send signals to shut down it’s children
Iterates over the child pids in the frame and sends the SIGTERM signal to shut them down.
blackhole.utils.set_process_title()
Set the title of the process.
If the process is the master, set a master title, otherwise set worker.
7.6. blackhole.state
21
Blackhole Documentation, Release 1.6.0
blackhole.utils.email_id()
Generate an HEX ID to assign to each connection.
Will be reused later down the line due to the limited number of characters.
22
Chapter 7. Reference
CHAPTER
EIGHT
INDICES AND TABLES
• genindex
• modindex
• search
23
Blackhole Documentation, Release 1.6.0
24
Chapter 8. Indices and tables
PYTHON MODULE INDEX
b
blackhole.connection (Unix), 19
blackhole.data (Unix), 19
blackhole.log (Unix), 20
blackhole.opts (Unix), 20
blackhole.ssl_utils (Unix), 20
blackhole.state (Unix), 21
blackhole.utils (Unix), 21
25