updates for producing a working router live cd

This commit is contained in:
Mark McClain 2012-09-23 10:26:05 -04:00
parent 65afee0b7c
commit 0472cd07c1
9 changed files with 226 additions and 195 deletions

View File

@ -1,15 +1,26 @@
"""Set up the API server application instance
"""
import logging
import logging.handlers
import daemon
import lockfile
import flask
from akanda.router.api import v1
from akanda.router.manager import manager
handler = logging.handlers.TimedRotatingFileHandler(
'/var/log/akanda', when='D', interval=1, backupCount=10)
handler.setLevel(logging.INFO)
handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s %(lineno)d]'))
app = flask.Flask(__name__)
app.register_blueprint(v1.base)
app.register_blueprint(v1.system)
app.register_blueprint(v1.firewall)
app.logger.addHandler(handler)
@app.before_request
def attach_config():
@ -20,6 +31,15 @@ def attach_config():
def main():
#TODO(mark): make this use a config file
app.debug = True
app.run(host='0.0.0.0', port=5000)
app.debug = False
#TODO(mark): make this use a config file ie
# app.config.from_object('akanda.router.config.Default')
# manager.state_path = app.config['STATE_PATH']
app.run(host=manager.management_address(ensure_configuration=True),
port=5000)
def daemonize():
pidfile = lockfile.FileLock('/var/run/akanda.pid')
with daemon.DaemonContext(pidfile=pidfile, stdout=file('/tmp/stdout', 'w+'), stderr=file('/tmp/stderr', 'w+')):
main()

View File

@ -9,25 +9,14 @@ def configure_ssh():
"""
mgr = ifconfig.InterfaceManager()
interfaces = mgr.get_interfaces(['em', 're'])
interfaces.sort(key=lambda x: x.ifname)
primary = interfaces[0]
listen_ip = mgr.get_management_address(ensure_configuration=True)
if not primary.is_up:
mgr.up(primary)
primary = mgr.get_interface(primary)
for address in primary.addresses:
if str(address.ip).startswith('fe80'):
listen_ip = '%s%%%s' % (address.ip, primary.ifname)
else:
sys.stderr.write('Unable to bring up first interface (%s)!\n' %
primary.ifname)
if not listen_ip:
sys.stderr.write('Unable to bring up first interface (ge0)!\n')
sys.exit(1)
config = open('/etc/ssh/sshd_config', 'r').read()
config = re.sub('(^|\n)(#)?(ListenAddress|AddressFamily) .*', '', config)
config += '\n'.join(
['ListenAddress %s' % listen_ip, 'AddressFamily inet6'])
config += '\n'.join(['AddressFamily inet6', 'ListenAddress ' + listen_ip])
open('/etc/ssh/sshd_config', 'w+').write(config)
sys.stderr.write('sshd configured to listen on %s\n' % listen_ip)

22
akanda/router/defaults.py Normal file
View File

@ -0,0 +1,22 @@
SSH = 22
SMTP = 25
DNS = 53
HTTP = 80
HTTPS = 443
HTTP_ALT = 8080
API_SERVICE = 5000
NFS_DEVELOPMENT = [111, 1110, 2049, 4045]
MANAGEMENT_PORTS = [SSH, API_SERVICE] #+ NFS_DEVELOPMENT
OUTBOUND_TCP_PORTS = [SSH, SMTP, HTTP, HTTPS, HTTP_ALT]
OUTBOUND_UDP_PORTS = [DNS]
BASE_RULES = [
'set skip on lo',
'match in all scrub (no-df)',
'block log (all)', # FIXME: remove log (all)
'pass proto icmp6 all',
'pass inet proto icmp icmp-type { echoreq, unreach }'
]

View File

@ -8,6 +8,7 @@ from akanda.router.drivers import base
GENERIC_IFNAME = 'ge'
PHYSICAL_INTERFACES = ['em', 're', 'en']
ULA_PREFIX = 'fdca:3ba5:a17a:acda::/64'
class InterfaceManager(base.Manager):
@ -68,6 +69,7 @@ class InterfaceManager(base.Manager):
def up(self, interface):
real_ifname = self.generic_to_host(interface.ifname)
self.sudo(real_ifname, 'up')
return self.get_interface(interface.ifname)
def down(self, interface):
real_ifname = self.generic_to_host(interface.ifname)
@ -75,9 +77,9 @@ class InterfaceManager(base.Manager):
def update_interface(self, interface):
real_ifname = self.generic_to_host(interface.ifname)
old_interface = self.get_interface(real_ifname)
old_interface = self.get_interface(interface.ifname)
self._update_description(real_ifname, interface)
#self._update_description(real_ifname, interface)
self._update_groups(real_ifname, interface, old_interface)
# Must update primary before aliases otherwise will lose address
# in case where primary and alias are swapped.
@ -94,10 +96,14 @@ class InterfaceManager(base.Manager):
add, delete)
def _update_addresses(self, real_ifname, interface, old_interface):
add = lambda a: (real_ifname, 'alias',
str(a.ip), 'prefixlen', a.prefixlen)
delete = lambda a: (real_ifname, '-alias',
str(a.ip), 'prefixlen', a.prefixlen)
#TODO (mark): add support for IPv6 inet inet6 add delete
family = {4: 'inet', 6: 'inet6'}
add = lambda a: (real_ifname, family[a.version], str(a.ip),
'prefixlen', a.prefixlen, 'alias')
delete = lambda a: (real_ifname, family[a.version], str(a.ip),
'prefixlen', a.prefixlen, '-alias')
self._update_set(real_ifname, interface, old_interface,
'addresses', add, delete)
@ -112,10 +118,25 @@ class InterfaceManager(base.Manager):
return
for item in (next_set - prev_set):
self.sudo(fmt_args_add(item))
self.sudo(*fmt_args_add(item))
for item in (prev_set - next_set):
self.sudo(fmt_args_delete(item))
self.sudo(*fmt_args_delete(item))
def get_management_address(self, ensure_configuration=False):
primary = self.get_interface(GENERIC_IFNAME + '0')
prefix, prefix_len = ULA_PREFIX.split('/', 1)
eui = netaddr.EUI(primary.lladdr)
ip_str = str(eui.ipv6_link_local()).replace('fe80::', prefix[:-1])
if not primary.is_up:
self.up(primary)
ip = netaddr.IPNetwork('%s/%s' % (ip_str, prefix_len))
if ensure_configuration and ip not in primary.addresses:
primary.addresses.append(ip)
self.update_interface(primary)
return ip_str
def _parse_interfaces(data, filters=None):
@ -125,7 +146,7 @@ def _parse_interfaces(data, filters=None):
continue
for f in filters or ['']:
if iface_data.startswith(f):
if iface_data.startswith(f) and iface_data[len(f)].isdigit():
break
else:
continue

View File

@ -48,8 +48,10 @@ class PFManager(base.Manager):
return self._show('m')
def update_conf(self, conf_data):
replace_file('/tmp/pf.ctl', conf_data)
execute(['mv', '/tmp/pf.ctl', '/etc/pf.ctl'], self.root_helper)
replace_file('/tmp/pf.conf', conf_data)
execute(['mv', '/tmp/pf.conf', '/etc/pf.conf'], self.root_helper)
self.sudo('-f', '/etc/pf.conf')
class TableManager(base.Manager):

View File

@ -7,6 +7,7 @@ from akanda.router.drivers import dnsmasq, ifconfig, pf
DHCP_DIR = 'dhcp'
PF_FILENAME = 'pf.conf'
class Manager(object):
def __init__(self, state_path='.'):
self.state_path = os.path.abspath(state_path)
@ -14,6 +15,9 @@ class Manager(object):
self.if_mgr.ensure_mapping()
self._config = models.Configuration()
def management_address(self, ensure_configuration=False):
return self.if_mgr.get_management_address(ensure_configuration)
@property
def config(self):
return self._config
@ -29,7 +33,7 @@ class Manager(object):
#TODO(mark): update_vpn
def update_interfaces(self):
self.if_mgr.update_interfaces(sel.config.interfaces)
self.if_mgr.update_interfaces(self.config.interfaces)
def update_dhcp(self):
pass
@ -63,7 +67,7 @@ class Manager(object):
rules.extend(
['%s = "%s"' % i for i in self.if_mgr.generic_mapping.items()])
rules.append(re.sub('(\s)(ge\d+[\s:])', r'\1$\2', virt_data))
rules.append(re.sub('([\s!])(ge\d+([\s:]|$))', r'\1$\2', virt_data))
return '\n'.join(rules)

View File

@ -5,6 +5,9 @@ import StringIO
import netaddr
from akanda.router import defaults
GROUP_NAME_LENGTH = 15
class ModelBase(object):
__metaclass__ = abc.ABCMeta
@ -22,7 +25,7 @@ class Interface(ModelBase):
self.ifname = ifname
self.description = description
self.addresses = addresses
self.groups = groups or []
self.groups = [g[:GROUP_NAME_LENGTH] for g in (groups or [])]
self.flags = flags or []
self.lladdr = lladdr
self.mtu = mtu
@ -374,11 +377,11 @@ class Configuration(ModelBase):
@property
def interfaces(self):
return [n.interface for n in self.networks if n.iterface]
return [n.interface for n in self.networks if n.interface]
@property
def pf_config(self):
rv = ['block']
rv = defaults.BASE_RULES[:]
# add default deny all external networks and remember 1st for nat
ext_if = None
@ -387,14 +390,16 @@ class Configuration(ModelBase):
ext_if = n.interface.ifname
break
# add in nat rules
# add in nat and management rules
for network in self.networks:
if network.network_type == Network.EXTERNAL:
continue
elif network.network_type == Network.INTERNAL:
if ext_if:
rv.append(
rv.extend(
_format_nat_rule(ext_if, network.interface.ifname))
elif network.network_type == Network.MANAGEMENT:
rv.extend(_format_mgt_rule(network.interface.ifname))
else:
# isolated and management nets block all between interfaces
rv.append(_format_isolated_rule(network.interface.ifname))
@ -404,11 +409,23 @@ class Configuration(ModelBase):
# add anchors and rules
rv.extend(a.pf_rule for a in self.anchors)
return '\n'.join(rv)
return '\n'.join(rv) + '\n'
def _format_nat_rule(ext_if, int_if):
return ('pass out on %s from %s:network to any nat-to %s' %
(ext_if, int_if, ext_if))
tcp_ports = ', '.join(str(p) for p in defaults.OUTBOUND_TCP_PORTS)
udp_ports = ', '.join(str(p) for p in defaults.OUTBOUND_UDP_PORTS)
return [('pass out on %s from %s:network to any nat-to %s' %
(ext_if, int_if, ext_if)),
'pass in on %s proto tcp to any port {%s}' % (int_if, tcp_ports),
'pass in on %s proto udp to any port {%s}' % (int_if, udp_ports)]
def _format_mgt_rule(mgt_if):
ports = ', '.join(str(p) for p in defaults.MANAGEMENT_PORTS)
return [('pass quick proto tcp from %s:network to %s port { %s }' %
(mgt_if, mgt_if, ports)),
'block quick from !%s to %s:network' % (mgt_if, mgt_if)]
def _format_isolated_rule(int_if):
return 'block from %s:network to any' % int_if

View File

@ -9,13 +9,13 @@
# \/ \/ \/ \/ \/ \/
#
# This script creates an Akanda Live CD - powered by OpenBSD, Python, and
# Twisted - and # lets you customize it.
# Flask - and # lets you customize it.
#
# Copyright (c) 2009 Reiner Rottmann. Released under the BSD license.
# Copyright (c) 2012 New Dream Network, LLC (DreamHost).
#
# First release 2009-06-20
# Akanda release 2012-07-29
# Akanda release 2012-10-14
#
# Notes:
#
@ -27,20 +27,23 @@
###############################################################################
MAJ=5 # Version major number
MIN=1 # Version minor number
ARCH=i386 # Architecture
TZ=US/Eastern # Time zones are in /usr/share/zoneinfo
ARCH=amd64 # Architecture
TZ=UTC # Time zones are in /usr/share/zoneinfo
# The base sets that should be installed on the akanda live cd
SETS="base etc man"
# Additional packages that should be installed on the akanda live cd
PACKAGES="python-2.7.1p12 rsync-3.0.9 git py-pip gmake curl wget"
PACKAGES="ntp python-2.7.1p12 py-pip wget"
WDIR=/tmp/akanda-livecdx # Working directory
WDIR=/usr/local/akanda-livecdx # Working directory
CDBOOTDIR=$WDIR/$MAJ.$MIN/$ARCH # CD Boot directory
OUTDIR=/tmp
HERE=`pwd`
# Mirror to use to download the OpenBSD files
#BASEURL=http://ftp-stud.fht-esslingen.de/pub/OpenBSD
BASEURL=http://openbsd.mirrors.pair.com
BASEURL=http://192.168.57.254:8000/OpenBSD
MIRROR=$BASEURL/$MAJ.$MIN/$ARCH
PKG_PATH=$BASEURL/$MAJ.$MIN/packages/$ARCH
DNS=8.8.8.8 # Google DNS Server to use in live cd (change accordingly)
@ -89,7 +92,7 @@ function usage {
echo -e " -W :\tselect working directory (default: $WDIR)" >&2
echo >&2
echo -e "Example:" >&2
echo -e "# $SCRIPTNAME -A i386 -M 4 -m 5 -W /tmp/livecd" >&2
echo -e "# $SCRIPTNAME -A amd64 -M 4 -m 5 -W /tmp/livecd" >&2
echo >&2
[[ $# -eq 1 ]] && exit $1 || exit $EXIT_FAILED
}
@ -199,16 +202,6 @@ EOF
Welcome to Akanda: Powered by OpenBSD.
EOF
echo "[*] Creating dhcp client configuration..."
cat >$WDIR/etc/dhclient.conf <<EOF
initial-interval 1;
request subnet-mask,
broadcast-address,
routers, domain-name,
domain-name-servers,
host-name;
EOF
echo "[*] Setting name..."
@ -216,11 +209,6 @@ EOF
akanda
EOF
echo "[*] Setting hostname.em0 to dhcp..."
cat > $WDIR/etc/hostname <<EOF
dhcp
EOF
echo "[*] Modifying rc.local..."
cat >>$WDIR/etc/rc.local <<EOF
# If you have enough memory, this speeds up some bins, but you must
@ -235,65 +223,22 @@ EOF
# /bin/cp -rp /sbin /binmfs
# fi
# Select keyboard language
echo "Select keyboard language (by number):"
select klang in us de es it fr be jp nl ru uk sv no pt br hu tr dk sg pl sf lt la lv
do
/sbin/kbd \$klang
break
done
# set keyboard to US
echo "Setting keyboard language to us:"
/sbin/kbd us
# function for setting the timezone
sub_timezone() {
# set TZ
rm /etc/localtime
ln -sf /usr/share/zoneinfo/$TZ /etc/localtime
while :
do
echo -n "What timezone are you in? ('?' for list) "
read zone
echo "Enabling forwarding..."
sed 's/^#net.inet.ip.forw/net.inet.ip.forw/g' /etc/sysctl.conf > /etc/sysctl.temp
mv /etc/sysctl.temp /etc/sysctl.conf
sed 's/^#net.inet6.ip6.forw/net.inet6.ip6.forw/g' /etc/sysctl.conf > /etc/sysctl.temp
mv /etc/sysctl.temp /etc/sysctl.conf
if [ \${zone} = "?" ]
then
ls -F /usr/share/zoneinfo
fi
if [ -d /usr/share/zoneinfo/\${zone} ]
then
ls -F /usr/share/zoneinfo/\${zone}
echo -n "What sub-timezone of \${zone} are you in? "
read subzone
zone="\${zone}/\${subzone}"
fi
if [ -f /usr/share/zoneinfo/\${zone} ]
then
echo "Setting local timezone to \${zone} ..."
rm /etc/localtime
ln -sf /usr/share/zoneinfo/\${zone} /etc/localtime
echo "done"
return
fi
done
}
# Select timezone
echo -n "Do you want to configure the timezone? (y/N): "
read timeconf
if [ ! -z \$timeconf ]
then
if [ \$timeconf = "y" ] || [ \$timeconf = "Y" ] || [ \$timeconf = "yes"] || [ \$timeconf = "Yes" ]
then
sub_timezone
fi
fi
# Configure network interface
myif=\$(ifconfig | awk -F: '/^[a-z][a-z]+[0-3]: flags=/ { print \$1 }' | grep -v lo | grep -v enc | grep -v pflog)
for thisif in \$myif
do
ifconfig \$thisif up
echo "starting dhclient \$thisif in background"
dhclient -q \$thisif 2>/dev/null &
done
echo "Configuring sshd for management interface..."
/usr/local/bin/akanda-configure-ssh
# If you have enough memory, you can populate /usr/local to RAM
if [ \$mymem -gt 500000000 ]
@ -315,96 +260,105 @@ then
fi
fi
# Password for root
echo -n "Do you want to set a password for root(y/N)?"
read rootpass
if [ ! -z \$rootpass ]
then
if [ \$rootpass = "y" ] || [ \$rootpass = "Y" ] || [ \$rootpass = "Yes" ] || [ \$rootpass = "yes" ] || [ \$rootpass = "YES" ]
then
passwd root
fi
else
echo "password for root not set (password empty)"
fi
EOF
echo "[*] Modifying the library path..."
echo >> $WDIR/root/.cshrc << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
echo >> $WDIR/root/.profile << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
echo >> $WDIR/etc/profile/.cshrc << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
echo >> $WDIR/etc/profile/.profile << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
echo "[*] Using DNS ($DNS) in livecd environment..."
echo "nameserver $DNS" > $WDIR/etc/resolv.conf
echo "[*] Installing additional packages..."
cat > $WDIR/tmp/packages.sh <<EOF
#!/bin/sh
export PKG_PATH=$(echo $PKG_PATH | sed 's#\ ##g')
for i in $PACKAGES
do
pkg_add -i \$i
done
EOF
echo "[*] Copying akanda to /var/..."
cp -r /root/repos/akanda $WDIR/var
echo "[*] Modifying the library path..."
cat > $WDIR/root/.cshrc << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
cat > $WDIR/root/.profile << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
cat > $WDIR/etc/profile/.cshrc << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
cat > $WDIR/etc/profile/.profile << EOF
# Workaround for missing libraries:
export LD_LIBRARY_PATH=/usr/local/lib
EOF
echo "[*] Using DNS ($DNS) in livecd environment..."
echo "nameserver $DNS" > $WDIR/etc/resolv.conf
chmod +x $WDIR/tmp/packages.sh
chroot $WDIR /tmp/packages.sh
rm $WDIR/tmp/packages.sh
echo "[*] Disabling services...."
cat > $WDIR/etc/rc.conf.local <<EOF
spamlogd_flags=NO
inetd=NO
amd_master=NO
EOF
echo "[*] Entering Akanda livecd builder (chroot environment)."
echo "[*] Once you have finished your modifications, type \"exit\""
cat <<EOF
**These steps will eventually be automated as part of the build process in further revisions**
Setup environment:
echo "[*] Setting default password..."
cp $HERE/etc/master.passwd $WDIR/etc/master.passwd
cp $HERE/etc/passwd $WDIR/etc/passwd
cp $HERE/etc/group $WDIR/etc/group
chroot $WDIR passwd root
echo "[*] Installing additional packages..."
cat > $WDIR/tmp/packages.sh <<EOF
#!/bin/sh
export PKG_PATH=$(echo $PKG_PATH | sed 's#\ ##g')
for i in $PACKAGES
do
pkg_add -i \$i
done
EOF
To get rid load libraries erros, run:
chmod +x $WDIR/tmp/packages.sh
chroot $WDIR /tmp/packages.sh
rm $WDIR/tmp/packages.sh
echo "[*] Installing akanda software..."
cat > $WDIR/tmp/akanda.sh <<EOF
#!/bin/sh
export LD_LIBRARY_PATH=/usr/local/lib
ln -sf /usr/local/bin/python2.7 /usr/local/bin/python
ln -sf /usr/local/bin/python2.7-2to3 /usr/local/bin/2to3
ln -sf /usr/local/bin/python2.7-config /usr/local/bin/python-config
ln -sf /usr/local/bin/pydoc2.7 /usr/local/bin/pydoc
ln -sf /usr/local/bin/pip-2.7 /usr/local/bin/pip
Install deps:
pip install netaddr repoze.lru txroutes
Install Akanda:
**You may have to generate a new ssh key and add it to github for the akanda install to continue**
cd /var
git clone git@github.com:dreamhost/akanda.git
cd akanda
gmake install-dev
cd /tmp/router_appliance && python setup.py install
EOF
chroot $WDIR
echo "[*] Disabling services...."
cat > $WDIR/etc/rc.conf.local <<EOF
spamlogd_flags=NO
inetd=NO
amd_master=NO
EOF
cp -r $HERE/../../router_appliance/ $WDIR/tmp
chmod +x $WDIR/tmp/akanda.sh
chroot $WDIR /tmp/akanda.sh
rm $WDIR/tmp/akanda.sh
rm -rf $WDIR/tmp
mkdir $WDIR/tmp
echo "[*] Add SSH autoconfig...."
cp $HERE/etc/rc.d/sshd $WDIR/etc/rc.d/sshd
echo "[*] Add rc.conf.local...."
cat > $WDIR/etc/rc.conf.local <<EOF
spamlogd_flags=NO
inetd=NO
amd_master=NO
EOF
echo "[*] Add rc.local file...."
cp $HERE/etc/rc.local $WDIR/etc/rc.local
echo "[*] Add up files...."
cat "up" > $WDIR/etc/hostname.em0
cat "up" > $WDIR/etc/hostname.re0
#echo "[*] Entering Akanda livecd builder (chroot environment)."
#echo "[*] Once you have finished your modifications, type \"exit\""
# chroot $WDIR
echo "[*] Deleting sensitive information..."
cd $WDIR && rm -i root/{.history,.viminfo}
@ -440,10 +394,10 @@ EOF
echo "[*] Creating Akanda live-cd iso..."
cd /
mkhybrid -l -R -o $WDIR/livecd$MAJ$MIN-$ARCH.iso -b $MAJ.$MIN/$ARCH/cdbr -c $MAJ.$MIN/$ARCH/boot.catalog $WDIR
mkhybrid -l -R -o $OUTDIR/livecd$MAJ$MIN-$ARCH.iso -b $MAJ.$MIN/$ARCH/cdbr -c $MAJ.$MIN/$ARCH/boot.catalog $WDIR
echo "[*] Your modified Akanda iso is in $WDIR/livecd$MAJ$MIN-$ARCH.iso"
ls -lh $WDIR/livecd$MAJ$MIN-$ARCH.iso
echo "[*] Your modified Akanda iso is in $OUTDIR/livecd$MAJ$MIN-$ARCH.iso"
ls -lh $OUTDIR/livecd$MAJ$MIN-$ARCH.iso
if [ $CLEANUP="yes" ];then
echo "[*] Cleanup"

View File

@ -14,6 +14,7 @@ setup(
install_requires=[
'flask>=0.9',
'netaddr>=0.7.7',
'python-daemon',
],
namespace_packages=['akanda'],
packages=find_packages(),
@ -24,6 +25,7 @@ setup(
'akanda-configure-ssh ='
'akanda.router.commands.management:configure_ssh',
'akanda-api-service =akanda.router.api.server:main',
'akanda-api-daemon =akanda.router.api.server:daemonize',
]
},
)