incognitomail

package module
v0.0.0-...-1048de1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2016 License: MIT Imports: 21 Imported by: 0

README

IncognitoMail

IncognitoMail is a proof of concept implementation for the Incognito Emails idea.

This is the back-end part of the implementation. It contains a server that should run in the same machine as your Message Transfer Agent (MTA). The front-end part is a browser add-on that communicates with the server in this project.

How it works

IncognitoMail has a very simple workflow. Whenever you need to create a new account, a secret token will be generated. Treat this token as that account's password! Whoever has access to this token will be able to manage your account's handles. To create a new account, you have to specify the email address to send all messages received by the future Incognito Emails. Whenever you need to create a new Incognito Email (a new handle), pass the secret token for your account. IncognitoMail will automatically generate a new address and update your MTA to redirect all messages received by this new address to the address registered with your account. It's that simple, and most of the work will be done by the browser add-on!

Project Status

This is a new project, stability is currently not guaranteed. Since it's in active development, parts may change (a lot). IncognitoMail currently only supports Postfix as the MTA. A task list with current and future features is available below. If you wish to use IncognitoMail, but need a certain feature added, please open an issue.

Current and future features and fixes

Checked boxes are features currently being developed. All other items are planned features and may be changed or removed.

  • Create tests for each module to ensure stability and robustness
  • Remove old accounts and handles that were never used
  • Check if a rate limiting feature for web sockets makes sense
  • Improve error messages returned to the user from the commandline tool
  • Add permission checking before changing stuff in the MTA
  • Improve logging
  • Support for multiple domains for the same MTA
  • Improve random string generation for secret tokens and handles

Installing

To use IncognitoMail, you will first need a working Postfix setup configured with either a canonical map or a virtual alias map.

  • A canonical map is best suited if you are delivering messages to the local server
  • A virtual alias map should be used if you plan to forward messages to another server, e.g. Gmail

This guide from ArsTechnica is great to follow and get your first Postfix setup running.

To get IncognitoMail, install Go and run:

$ go get github.com/danielsidhion/incognitomail/
$ go get github.com/danielsidhion/incognitomail/cmd/incognitomail

This will download the server code and the incognitomail command line utility.

Configuration

IncognitoMail can't function out of the box without configuration. You need to at least provide information about your MTA setup. The configuration file should be written in the INI format, which is very simple to use. A sample file with all possible configuration options is provided below for reference.

[General] ; Make sure to include the section name before that section's keys!
MailSystem = "postfix" ; For future reference only. Currently not used (server assumes postfix, will fail with any other value)
UnixSockPath = "/tmp/incognito.sock" ; Address of the unix socket used for communication between the daemon process and the cli utility
LockFilePath = "/var/lock/incognito.lock" ; Path to the file used to prevent two server processes from running at the same time
ListenPath = "/incognitomail" ; Path where the HTTP server will listen for websocket connections
ListenAddress = ":9090" ; Address for the HTTP server to listen. Always include the port number with the ":" prefix. An empty address (as in this case) will listen on all interfaces
TLSCertFile = "server.pem" ; If using HTTPS, path to the server certificate. If signed by a CA, this file needs to be the concatenation of the server's certificate, any intermediates and the CA's certificate
TLSKeyFile = "server.key" ; If using HTTPS, path to the private key file corresponding to the server certificate

[Persistence]
Type = "boltdb" ; For future reference only. Currently not used (server assumes boltdb, will fail with any other value)
DatabasePath = "incognito.db" ; Path to the file where all the information about accounts and handles is stored

[PostfixConfig]
Domain = "@sidhion.com" ; The same domain configured in Postfix
MapFilePath = "/tmp/postfix/canonical" ; Path to the map file used in Postfix. Can be either the canonical or the virtual alias map

Usage

incognitomail [-c|--config <path>] [command [arguments]]

  -c string
    	path to a configuration file (shorthand)
  -config string
    	path to a configuration file

If the command is empty, incognitomail will start a server process and begin listening for connections. Currently, available commands are:

  • new account <address>: creates a new account, and registers address as the main email address to send all messages. Will output the generated secret token for that account
  • new handle <secret>: creates a new handle for the account with the specified secret token
  • delete account <secret>: deletes the account with the registered secret
  • delete handle <handle> <secret>: deletes the handle handle associated with the account with the registered secret
  • list <secret>: lists all handles registered for a given account
  • stop: stop the current server process

Important: please make sure that you run the server instance with the same user/privileges as your Postfix setup! Generally, this will mean running the server instance as the postfix user, but you should check the file and folder permissions of your Postfix configuration to be sure.

Daemonization

It is possible to run IncognitoMail as a daemon with the help of a service manager. The only currently tested setup is with Upstart, but it shouldn't be much different from other managers. You can use the sample configuration provided below for Upstart (remember to modify it, specially the paths, to your setup):

description "IncognitoMail service"
author      "Daniel Sidhion"

start on filesystem or runlevel [2345]
stop on runlevel [!2345]

script
  echo $$ > /var/run/incognitomail.pid
  exec incognitomail -c /etc/incognitomail.conf
end script

pre-stop script
  rm /var/run/incognitomail.pid
end script

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (

	// Config holds all global configuration.
	Config = defaultConfig

	// ErrInvalidConfig is used when loading a configuration with invalid values.
	ErrInvalidConfig = errors.New("invalid configuration values")
)
View Source
var (
	// ErrEmptySecret is used when an empty account secret is used.
	ErrEmptySecret = errors.New("empty secret")

	// ErrEmptyTarget is used when an empty account target is used.
	ErrEmptyTarget = errors.New("empty target")

	// ErrAccountNotFound is used when an action requires an account to exist, but it wasn't found.
	ErrAccountNotFound = errors.New("account not found")

	// ErrAccountExists is used when trying to create an account with a given secret, but it already exists.
	ErrAccountExists = errors.New("account already exists")

	// ErrHandleNotFound is used when an action requires a handle to exist, but it wasn't found.
	ErrHandleNotFound = errors.New("handle not found")

	// ErrHandleExists is used when trying to create a handle, but it already exists.
	ErrHandleExists = errors.New("handle already exists")
)
View Source
var (
	// ErrServerNotStarted is used whenever an action is taken that expects a started server, but the server is actually not started.
	ErrServerNotStarted = errors.New("server not started")

	// ErrLockFileAlreadyExists is used only when trying to acquire a lock file, but the lock file already exists.
	ErrLockFileAlreadyExists = errors.New("lock file already exists")

	// ErrLockFileNotFound is used whenever an action requires a lock file to be used, but the lock file handle is nil.
	ErrLockFileNotFound = errors.New("could not find lock file")

	// ErrEmptyCommand is used when the server receives an empty command.
	ErrEmptyCommand = errors.New("empty command received")

	// ErrUnknownCommand is used when the server receives an unknown/garbage command.
	ErrUnknownCommand = errors.New("unknown command received")

	// ErrWrongCommand is used when a known command is received, but is malformed.
	ErrWrongCommand = errors.New("wrong command usage")

	// ErrInvalidPermission is used when a command has been received from the websocket, but the server shouldn't execute it.
	ErrInvalidPermission = errors.New("invalid permission to do this")
)

Functions

func CreateRPCServiceClient

func CreateRPCServiceClient() *gorpc.DispatcherClient

CreateRPCServiceClient creates and returns a reasy to use RPC dispatcher client.

func ReadConfigFromFile

func ReadConfigFromFile(path string) error

ReadConfigFromFile reads the file in the given path and parses all config data from it. Any value not defined in this configuration file will be kept as its default value.

func ReadConfigFromReader

func ReadConfigFromReader(reader io.Reader) error

ReadConfigFromReader parses all config data from the given reader. Any value not defined in the read string will be kept as its default value.

func ResetConfig

func ResetConfig()

ResetConfig switches all values back to the default.

func ValidConfig

func ValidConfig() bool

ValidConfig returns true if the current Config is valid, i.e. not likely to crash the server.

Types

type IncognitoData

type IncognitoData struct {
	// contains filtered or unexported fields
}

IncognitoData holds a "connection" to the persistence layer. To create a valid IncognitoData object, call OpenIncognitoData().

func OpenIncognitoData

func OpenIncognitoData() (*IncognitoData, error)

OpenIncognitoData returns an IncognitoData object with a successful "connection" to the persistence layer, ready to be used.

func (*IncognitoData) Close

func (a *IncognitoData) Close()

Close closes the "connection" with the persistence layer.

func (*IncognitoData) DeleteAccount

func (a *IncognitoData) DeleteAccount(secret string)

DeleteAccount deletes all information related to the account with the given secret. If no account with that secret exists, it does nothing.

func (*IncognitoData) DeleteAccountHandle

func (a *IncognitoData) DeleteAccountHandle(secret, handle string)

DeleteAccountHandle deletes the given handle from the account with the given secret. If either the account or the handle does not exist, this does nothing.

func (*IncognitoData) GetAccountTarget

func (a *IncognitoData) GetAccountTarget(secret string) (string, error)

GetAccountTarget returns the target registered for the account with the given secret.

func (*IncognitoData) HasAccount

func (a *IncognitoData) HasAccount(secret string) bool

HasAccount returns true if an account with the given secret exists, false otherwise.

func (*IncognitoData) HasHandleGlobal

func (a *IncognitoData) HasHandleGlobal(handle string) bool

HasHandleGlobal returns true if the given handle exists for any account, false otherwise.

func (*IncognitoData) ListAccountHandles

func (a *IncognitoData) ListAccountHandles(secret string) ([]string, error)

ListAccountHandles returns an array with all handles from the account with the given secret.

func (*IncognitoData) NewAccount

func (a *IncognitoData) NewAccount(secret, target string) error

NewAccount generates a new account with the given secret and target email address.

func (*IncognitoData) NewAccountHandle

func (a *IncognitoData) NewAccountHandle(secret, handle string) error

NewAccountHandle stores the given handle for the account with the given secret.

type MailSystemHandleWriter

type MailSystemHandleWriter interface {
	AddHandle(string, string) (string, error)
	RemoveHandle(string) error
}

MailSystemHandleWriter has methods for adding and removing mappings from the mail system.

type PostfixWriter

type PostfixWriter struct {
	// contains filtered or unexported fields
}

PostfixWriter holds all the information required to add or remove handles to a postfix system.

func NewPostfixWriter

func NewPostfixWriter() *PostfixWriter

NewPostfixWriter returns a PostfixWriter object initialized with values from the config.

func (*PostfixWriter) AddHandle

func (p *PostfixWriter) AddHandle(h string, t string) (string, error)

AddHandle adds a handle to the map file.

func (*PostfixWriter) RemoveHandle

func (p *PostfixWriter) RemoveHandle(h string) error

RemoveHandle scans a map file for a line starting with the handle and removes it.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server holds all the data required to run the server.

func NewServer

func NewServer() (*Server, error)

NewServer returns an IncognitoMailServer object ready for use.

func (*Server) DeleteAccount

func (s *Server) DeleteAccount(secret string) error

DeleteAccount deletes all data from the account with the given secret. If the account does not exist, it returns an error.

func (*Server) DeleteHandle

func (s *Server) DeleteHandle(secret, handle string) error

DeleteHandle deletes the given handle from the account with the given secret. If the account does not exist, it returns an error.

func (*Server) ListHandles

func (s *Server) ListHandles(secret string) ([]string, error)

ListHandles returns all handles from the account with the given secret.

func (*Server) NewAccount

func (s *Server) NewAccount(target string) (string, error)

NewAccount creates a new account with the given target email address and returns the secret.

func (*Server) NewHandle

func (s *Server) NewHandle(accountSecret string) (string, error)

NewHandle creates a new handle for the account with the given secret.

func (*Server) SendCommand

func (s *Server) SendCommand(source, args string) (string, error)

SendCommand is executed for every message received either by the websocket or RPC interface. Builds a well-defined command to send to the goroutine listening for commands to execute.

func (*Server) Start

func (s *Server) Start()

Start begins listening for websocket connections (external requests) and RPC calls (internal requests).

func (*Server) Stop

func (s *Server) Stop()

Stop will stop everything from a running server

func (*Server) Wait

func (s *Server) Wait() error

Wait blocks until the server has finished executing. If the server wasn't started, it returns an error instead.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL