initial commit of some preliminary notes

This commit is contained in:
Joshua Tauberer 2013-08-20 22:27:32 -04:00
commit d3a20b3369
8 changed files with 233 additions and 0 deletions

58
README.md Normal file
View file

@ -0,0 +1,58 @@
Mail in a Box
=============
One-click deployment of your own mail server and personal cloud (so to speak).
This draws heavily on Sovereign by Alex Payne (https://github.com/al3x/sovereign) and the "NSA-proof your email in 2 hours" blog post by Drew Crawford (http://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/).
Deploying to EC2 from the command line
--------------------------------------
Sign up for Amazon Web Services.
Create an Access Key at https://console.aws.amazon.com/iam/home?#security_credential. Download the key and save the information somewhere secure.
Set up your environment and paste in the two parts of your access key that you just downloaded:
sudo apt-get install ec2-api-tools
export AWS_ACCESS_KEY=your_access_key_id
export AWS_SECRET_KEY=your_secret_key
export EC2_URL=ec2.us-east-1.amazonaws.com
export AWS_AZ=us-east-1a
The first time around, create a new volume (disk drive) to store your stuff.
source ec2/new_volume.sh
If you want to reuse an existing volume:
export VOLUME_ID=...your existing volume id...
Here we're using the Ubuntu 13.04 amd64 instance-store-backed AMI in the us-east region. You can select another at http://cloud-images.ubuntu.com/locator/ec2/.
Generate a new "keypair" (if you don't have one) that will let you SSH into your machine after you start it:
ec2addkey mykey > mykey.pem
chmod go-rw mykey.pem
Then launch a new instance. We're creating a m1.small instance --- it's the smallest instance that can use an instance-store-backed AMI. So charges will start to apply.
source ec2/start_instance.sh
It will wait until the instance is available.
Log in:
ssh -i mykey.pem ubuntu@$INSTANCE_IP
Set up:
logout
Terminate your instance with:
ec2-terminate-instances $INSTANCE

6
ec2/new_volume.sh Normal file
View file

@ -0,0 +1,6 @@
export VOLUME_SIZE=1 # in GiB (2^30 bytes)
ec2-create-volume -s $VOLUME_SIZE -z $AWS_AZ > volume.info
export VOLUME_ID=`cat volume.info | awk {'print $2'}`
export VOLUME_IS_NEW=1
echo Created new volume: $VOLUME_ID

18
ec2/start_instance.sh Normal file
View file

@ -0,0 +1,18 @@
export AMI=`curl http://cloud-images.ubuntu.com/locator/ec2/releasesTable | python3 tools/get_ubunut_ami.py us-east-1 13.04 amd64 instance-store`
ec2run $AMI -k mykey -t m1.small -z $AWS_AZ | tee instance.info
export INSTANCE=`cat instance.info | grep INSTANCE | awk {'print $2'}`
sleep 5
while [ 1 ]
do
export INSTANCE_IP=`ec2-describe-instances $INSTANCE | grep INSTANCE | awk {'print $14'}`
if [ -z "$INSTANCE_IP" ]
then
echo "Waiting for $INSTANCE to start..."
else
exit
fi
sleep 6
done
echo New instance started: $INSTANCE_IP

55
scripts/mail.sh Normal file
View file

@ -0,0 +1,55 @@
# Configures a postfix SMTP server.
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y postfix postgrey
# TLS configuration
sudo tools/editconf.py /etc/postfix/main.cf \
smtpd_tls_auth_only=yes \
smtp_tls_security_level=may \
smtp_tls_loglevel=2 \
smtpd_tls_received_header=yes
# authorization via dovecot
sudo tools/editconf.py /etc/postfix/main.cf \
smtpd_sasl_type=dovecot \
smtpd_sasl_path=private/auth \
smtpd_sasl_auth_enable=yes \
smtpd_recipient_restrictions=permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination
sudo tools/editconf.py /etc/postfix/main.cf mydestination=localhost
# message delivery is directly to dovecot
sudo tools/editconf.py /etc/postfix/main.cf virtual_transport=lmtp:unix:private/dovecot-lmtp
# domain and user table is configured in a Sqlite3 database
sudo tools/editconf.py /etc/postfix/main.cf \
virtual_mailbox_domains=sqlite:/etc/postfix/virtual-mailbox-domains.cf \
virtual_mailbox_maps=sqlite:/etc/postfix/virtual-mailbox-maps.cf \
virtual_alias_maps=sqlite:/etc/postfix/virtual-alias-maps.cf \
local_recipient_maps=\$virtual_mailbox_maps
db_path=/home/ubuntu/storage/mail.sqlite
sudo su root -c "cat > /etc/postfix/virtual-mailbox-domains.cf" << EOF;
dbpath=$db_path
query = SELECT 1 FROM users WHERE email LIKE '@%s'
EOF
sudo su root -c "cat > /etc/postfix/virtual-mailbox-maps.cf" << EOF;
dbpath=$db_path
query = SELECT 1 FROM users WHERE email='%s'
EOF
sudo su root -c "cat > /etc/postfix/virtual-alias-maps.cf" << EOF;
dbpath=$db_path
query = SELECT destination FROM aliases WHERE source='%s'
EOF
# re-start postfix
sudo service postfix restart
# allow ports in the firewall
sudo ufw allow smtpd
sudo ufw allow submission

6
scripts/new_volume.sh Normal file
View file

@ -0,0 +1,6 @@
mkdir storage
# mount volume
echo "CREATE TABLE users (email text, password text);" | sqlite3 /home/ubuntu/storage/mail.sqlite;

27
scripts/system.sh Normal file
View file

@ -0,0 +1,27 @@
# Base system configuration.
sudo apt-get update
sudo apt-get -y upgrade
# Basic packages.
sudo apt-get -y install sqlite3
# Turn on basic services:
#
# ntp: keeps the system time correct
#
# fail2ban: scans log files for repeated failed login attempts and blocks the remote IP at the firewall
#
# These services don't need further configuration and are started immediately after installation.
sudo apt-get install -y ntp fail2ban
# Turn on the firewall. First allow incoming SSH, then turn on the firewall. Additional open
# ports will be set up in the scripts that set up those services.
sudo ufw allow ssh
sudo ufw allow domain
sudo ufw allow http
sudo ufw allow https
sudo ufw --force enable

37
tools/editconf.py Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/python3
import sys, re
# sanity check
if len(sys.argv) < 3:
print("usage: python3 editconf.py /etc/file.conf NAME=VAL [NAME=VAL ...]")
sys.exit(1)
# parse command line arguments
filename = sys.argv[1]
settings = sys.argv[2:]
# create the new config file in memory
found = set()
buf = ""
for line in open(filename):
for i in range(len(settings)):
name, val = settings[i].split("=", 1)
if re.match("\s*" + re.escape(name) + "\s*=", line):
buf += "#" + line
if i in found: break # we've already set the directive
buf += name + "=" + val + "\n"
found.add(i)
break
else:
# did not match any setting name
buf += line
for i in range(len(settings)):
if i not in found:
buf += settings[i] + "\n"
# Write out the new file.
with open(filename, "w") as f:
f.write(buf)

26
tools/get_ubuntu_ami.py Normal file
View file

@ -0,0 +1,26 @@
import sys, json, re
# Arguments:
region, version, arch, instance_type = sys.argv[1:]
# Read bytes from stdin.
dat = sys.stdin.read()
# Be flexible. The Ubuntu AMI list is invalid JSON by having a comma
# following the last element in a list.
dat = re.sub(r",(\s*)\]", r"\1]", dat)
# Parse JSON.
dat = json.loads(dat)
for item in dat["aaData"]:
if item[0] == region and item[2] == version and item[3] == arch and item[4] == instance_type:
ami_link = item[6]
# The field comes in the form of <a href="...">ami-id</a>
ami_link = re.sub(r"<.*?>", "", ami_link)
print(ami_link)
break