Script for automatically configuring a SmartOS zone as a router. See writeup mentioned at the top of the script.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# SEE WRITEUP AT https://davi.dyoung.tech/index.php/posts/configuring-smartos-as-a-router-with-ospf-and-dhcp | |
echo "This is the mega router installer script. Yay!" | |
# find out whether we're a one-nic router or a two-nic router | |
read -p "How many nics do we have? [1/2] " nicn | |
case $nicn in | |
1) | |
TWO_NICS=0 | |
;; | |
2) | |
TWO_NICS=1 | |
;; | |
*) | |
echo "c'mon, you have to answer this question" | |
exit 0 | |
;; | |
esac | |
# if we have two nics then that means we must route things to the other interface | |
# where does the other interface live? | |
if [ $TWO_NICS == 1 ] | |
then | |
read -p "Enter the IPv4 address of net1: " NET1_IPv4 | |
read -p "Enter the network number of net1: " NET1_IPv4_NETWORK | |
read -p "Enter the netmask *number* of net1, as /24 syntax: " NET1_IPv4_NETMASK | |
read -p "Enter the long form of this, e.g. 255.255.255.0: " NET1_IPv4_NETMASK_LONG | |
read -p "Enter the IPv6 address of net1: " NET1_IPv6 | |
read -p "Enter the IPv6 network number of net1 (assume /64): " NET1_IPv6_NETWORK | |
# net1 probably wants a dhcp server... | |
read -p "Enter DHCPv4 pool start address: " NET1_DHCP4_START | |
read -p "Enter DHCPv4 pool end address: " NET1_DHCP4_END | |
read -p "Enter DHCPv6 pool start address: " NET1_DHCPv6_START | |
read -p "Enter DHCPv6 pool end address: " NET1_DHCPv6_STOP | |
# oh yeah, a dns subdomain... | |
read -p "Enter the DNS subnet name (e.g. lan): " DNS_PREFIX | |
else | |
read -p "Enter the extra IPv4 network reachable from here (e.g. 10.0.0.0/12): " MAGIC_ROUTABLE_IPv4 | |
read -p "Enter the extra IPv6 network reachable from here (e.g. 2001::/16): " MAGIC_ROUTABLE_IPv6 | |
fi | |
echo "Fixing /root/.vimrc..." | |
( cat << EOF | |
colorscheme desert | |
set nocompatible " Must come first because it changes other options. | |
syntax enable " Turn on syntax highlighting. | |
filetype plugin on " Turn on file type detection. | |
set showcmd " Display incomplete commands. | |
set showmode " Display the mode you're in. | |
set backspace=indent,eol,start " Intuitive backspacing. | |
set hidden " Handle multiple buffers better. | |
set wildmenu " Enhanced command line completion. | |
set wildmode=list:longest " Complete files like a shell. | |
set incsearch " Highlight matches as you type. | |
set hlsearch " Highlight matches. | |
set wrap " Turn on line wrapping. | |
set modeline " Allow per file config | |
EOF | |
) > /root/.vimrc | |
echo "Fixing /root/.bashrc..." | |
( cat << "EOF" | |
if [ "$PS1" ]; then | |
shopt -s checkwinsize cdspell extglob histappend | |
alias ll='ls -lF' | |
alias ls='ls --color=auto' | |
HISTCONTROL=ignoreboth | |
HISTIGNORE="[bf]g:exit:quit" | |
PS1="[\u@\h \w]\\$ " | |
if [ -n "$SSH_CLIENT" ]; then | |
PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAME%%\.*} \007" && history -a' | |
fi | |
fi | |
alias ls='ls -laph --color' | |
EOF | |
) > /root/.bashrc | |
echo "Running pkgin to fetch updates..." | |
pkgin update | |
pkgin upgrade | |
echo "Installing packages..." | |
pkgin install mtr quagga | |
if [ $TWO_NICS ==1 ] | |
then | |
pkgin install isc-dhcpd | |
fi | |
# Configuring Zebra | |
echo "Configuring Zebra..." | |
( cat << EOF | |
! | |
! Zebra configuration saved from vty | |
! 2018/01/05 02:05:18 | |
! | |
log syslog | |
! | |
interface lo0 | |
no link-detect | |
! | |
interface net0 | |
no link-detect | |
ipv6 nd suppress-ra | |
ipv6 nd ra-interval 10 | |
ipv6 nd prefix 2001:470:xxxx:2099::/64 | |
! | |
EOF | |
) > /opt/local/etc/zebra/zebra.conf | |
if [ $TWO_NICS == 0 ] | |
then | |
( cat << EOF | |
interface net1 | |
no link-detect | |
ip address ${NET1_IPv4}/${NET1_IPv4_NETMASK} | |
no ipv6 nd suppress-ra | |
ipv6 nd managed-config-flag | |
ipv6 address ${NET1_IPv6_NETWORK}/64 | |
ipv6 nd prefix ${NET1_IPv6_NETWORK}/64 | |
! | |
! | |
! | |
line vty | |
! | |
EOF | |
) >> /opt/local/etc/zebra/zebra.conf | |
fi | |
# Configuring OSPF | |
echo "Configuring OSPF..." | |
if [ $TWO_NICS == 0 ] | |
then | |
( cat << EOF | |
! | |
! Zebra configuration saved from vty | |
! 2018/01/05 02:05:18 | |
! | |
log syslog | |
! | |
interface lo0 | |
! | |
interface net0 | |
! | |
router ospf | |
redistribute connected | |
network 172.20.99.0/24 area 0.0.0.0 | |
network ${MAGIC_ROUTABLE_IPv4} area 0.0.0.0 | |
! | |
line vty | |
! | |
EOF | |
) > /opt/local/etc/zebra/ospfd.conf | |
else | |
( cat << EOF | |
! | |
! Zebra configuration saved from vty | |
! 2018/01/05 02:05:18 | |
! | |
log syslog | |
! | |
interface lo0 | |
! | |
interface net0 | |
! | |
interface net1 | |
! | |
router ospf | |
redistribute connected | |
passive-interface net1 | |
network 172.20.99.0/24 area 0.0.0.0 | |
network ${NET1_IPv4_NETWORK}/${NET1_IPv4_NETMASK} area 0.0.0.0 | |
! | |
line vty | |
! | |
EOF | |
) > /opt/local/etc/zebra/ospfd.conf | |
fi | |
# Configuring OSPF6 | |
echo "Configuring OSPF6..." | |
if [ $TWO_NICS == 0 ] | |
then | |
( cat << EOF | |
! | |
! Zebra configuration saved from vty | |
! 2018/01/06 23:43:18 | |
! | |
log syslog | |
! | |
interface net0 | |
! | |
debug ospf6 lsa unknown | |
! | |
router ospf6 | |
redistribute static | |
redistribute connected | |
redistribute kernel | |
area 0.0.0.0 range 2001:470:xxxx:2099::/64 | |
area 0.0.0.0 range ${MAGIC_ROUTABLE_IPv6}/64 | |
! | |
interface net0 area 0.0.0.0 | |
! | |
line vty | |
! | |
EOF | |
) > /opt/local/etc/zebra/ospf6d.conf | |
else | |
( cat << EOF | |
! | |
! Zebra configuration saved from vty | |
! 2018/01/06 23:43:18 | |
! | |
log syslog | |
! | |
interface net0 | |
! | |
interface net1 | |
! | |
debug ospf6 lsa unknown | |
! | |
router ospf6 | |
redistribute static | |
redistribute connected | |
redistribute kernel | |
area 0.0.0.0 range 2001:470:xxxx:2099::/64 | |
area 0.0.0.0 range ${${NET1_IPv6_NETWORK}/64 | |
! | |
interface net0 area 0.0.0.0 | |
! | |
line vty | |
! | |
EOF | |
) > /opt/local/etc/zebra/ospf6d.conf | |
fi | |
# Configuring routeadm | |
echo "Configuring routeadm..." | |
echo "Selecting OSPF routing services..." | |
routeadm -s routing-svcs="quagga:ospf quagga:ospf6" | |
echo "Enabling IPv{4,6} routing and forwarding..." | |
routeadm -e ipv4-forwarding -e ipv4-routing -e ipv6-routing -e ipv6-forwarding | |
echo "Updating routeadm..." | |
routeadm -u | |
# Configuring ndpd | |
if [ $TWO_NICS == 1 ] | |
then | |
echo "Configuring NDPd..." | |
( cat << EOF | |
if net1 AdvSendAdvertisements True | |
if net1 AdvManagedFlag True | |
if net1 StatelessAddrConfig True | |
prefix ${NET1_IPv6_NETWORK}/64 net1 | |
EOF | |
) > /etc/inet/ndpd.conf | |
fi | |
# Configuring isc-dhcpd | |
if [ $TWO_NICS == 1 ] | |
then | |
echo "Configuring ISC-DHCPd..." | |
echo "Creating /opt/local/etc/dhcp/dhcpd.conf" | |
( cat << EOF | |
# dhcpd.conf | |
# | |
# Sample configuration file for ISC dhcpd | |
# | |
# option definitions common to all supported networks... | |
option domain-name "${DNS_PREFIX}.dav1d.biz"; | |
option domain-search "example.com"; | |
option domain-name-servers 172.20.99.2, 9.9.9.9; | |
default-lease-time 600; | |
max-lease-time 7200; | |
# If this DHCP server is the official DHCP server for the local | |
# network, the authoritative directive should be uncommented. | |
authoritative; | |
# Use this to send dhcp log messages to a different log file (you also | |
# have to hack syslog.conf to complete the redirection). | |
log-facility local7; | |
subnet ${NET1_IPv4_NETMASK} netmask ${NET1_IPv4_NETMASK_LONG} { | |
range ${NET1_DHCP4_START} ${NET1_DHCP4_END}; | |
option routers ${NET1_IPv4}; | |
} | |
EOF | |
) > /opt/local/etc/dhcp/dhcpd.conf | |
# Configuring isc-dhcpd6 | |
if [ $TWO_NICS == 1 ] | |
then | |
echo "Configuring ISC-DHCPd6..." | |
# Make an SMF service | |
echo "Making an SMF service" | |
# Make an XML file for the manifest | |
( cat << EOF | |
<?xml version='1.0'?> | |
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> | |
<service_bundle type='manifest' name='export'> | |
<service name='pkgsrc/isc-dhcpd6' type='service' version='0'> | |
<create_default_instance enabled='true'/> | |
<single_instance/> | |
<dependency name='network' grouping='require_all' restart_on='error' type='service'> | |
<service_fmri value='svc:/milestone/network:default'/> | |
</dependency> | |
<dependency name='filesystem-local' grouping='require_all' restart_on='none' type='service'> | |
<service_fmri value='svc:/system/filesystem/local:default'/> | |
</dependency> | |
<dependency name='config-file' grouping='require_all' restart_on='refresh' type='path'> | |
<service_fmri value='file:///opt/local/etc/dhcp/dhcpd6.conf'/> | |
</dependency> | |
<method_context> | |
<method_credential group='root' user='root'/> | |
</method_context> | |
<exec_method name='start' type='method' exec='/opt/local/lib/svc/method/isc-dhcpd6 start' timeout_seconds='30'/> | |
<exec_method name='stop' type='method' exec='/opt/local/lib/svc/method/isc-dhcpd6 stop' timeout_seconds='30'/> | |
<exec_method name='refresh' type='method' exec='/opt/local/lib/svc/method/isc-dhcpd6 refresh' timeout_seconds='30'/> | |
<template> | |
<common_name> | |
<loctext xml:lang='C'>ISC DHCP6 Server</loctext> | |
</common_name> | |
</template> | |
</service> | |
</service_bundle> | |
EOF | |
) > /root/isc-dhcpd6.xml | |
# Do some dodgy heredoc stuff to get the manifest into svccfg | |
svccfg << EOF | |
import /root/isc-dhcpd6.conf | |
end | |
EOF | |
# phew, that felt a shade evil | |
# Make an init script | |
echo "Making an init script" | |
( cat << "EOF" | |
#!/sbin/sh | |
# | |
# $NetBSD: isc-dhcpd.sh,v 1.1 2014/03/12 14:29:31 jperkin Exp $ | |
# | |
# Init script for isc-dhcpd. | |
# | |
case "$1" in | |
start) | |
if [ ! -d /var/db/isc-dhcp ]; then | |
mkdir -p /var/db/isc-dhcp | |
touch /var/db/isc-dhcp/dhcpd6.leases | |
chmod 0640 /var/db/isc-dhcp/dhcpd6.leases | |
fi | |
mkdir -p /var/run/isc-dhcp | |
chmod 0770 /var/run/isc-dhcp | |
/opt/local/sbin/dhcpd -6 -cf /opt/local/etc/dhcp/dhcpd6.conf | |
;; | |
stop) | |
if [ -s /var/run/isc-dhcp/isc-dhcpd6.pid ]; then | |
kill `cat /var/run/isc-dhcp/isc-dhcpd6.pid` | |
fi | |
;; | |
refresh) | |
$0 stop | |
$0 start | |
;; | |
esac | |
EOF | |
) > /opt/local/lib/svc/method/isc_dhcpd6 | |
chmod +x /opt/local/lib/svc/method/isc_dhcpd6 | |
# Make a config file | |
echo "Creating config file" | |
( cat << EOF | |
# Sample configuration file for ISC dhcpd | |
# | |
# option definitions common to all supported networks... | |
option dhcp6.domain-search "example.com"; | |
option dhcp6.name-servers 2001:470:xxxx:2099::2; | |
default-lease-time 600; | |
max-lease-time 7200; | |
# If this DHCP server is the official DHCP server for the local | |
# network, the authoritative directive should be uncommented. | |
authoritative; | |
# Use this to send dhcp log messages to a different log file (you also | |
# have to hack syslog.conf to complete the redirection). | |
log-facility local7; | |
subnet6 ${NET1_IPv6_NETWORK}/64 { | |
range6 ${NET1_DHCPv6_START} ${NET1_DHCPv6_STOP}; | |
ddns-domainname "${DNS_PREFIX}.dav1d.biz."; | |
ddns-rev-domainname "ip6.arpa."; | |
} | |
EOF | |
) > /opt/local/etc/dhcp/dhcpd6.conf | |
# Make a leases directory | |
echo "Making a leases directory" | |
mkdir -p /var/db/isc-dhcpd | |
# Touch a leases file | |
touch /var/db/isc-dhcpd/{dhcpd.leases,dhcpd.leases~,dhcpd6.leases,dhcpd6.leases~} | |
fi | |
# Generating boilerplate ipf config | |
echo "Generating ipfilter config files..." | |
echo "Writing ipf.conf" | |
( cat << EOF | |
# | |
# ipf.conf | |
# | |
# IP Filter rules to be loaded during startup | |
# | |
# See ipf(4) manpage for more information on | |
# IP Filter rules syntax. | |
# allow loopback | |
pass in quick on lo0 all | |
pass out quick on lo0 all | |
# pass all on net0 | |
pass in quick on net0 all | |
pass out quick on net0 all | |
# allow ping | |
pass in quick on net0 proto icmp from any to any | |
pass out quick on net0 proto icmp from any to any | |
# allow multicast for OSPF | |
pass in quick on net0 proto ospf from 172.20.99.0/24 to 224.0.0.5 | |
pass in quick on net0 proto ospf from 172.20.99.0/24 to 224.0.0.6 | |
pass out quick on net0 proto ospf from 172.20.99.0/24 to 224.0.0.5 | |
pass out quick on net0 proto ospf from 172.20.99.0/24 to 224.0.0.6 | |
# allow routernet traffic to bounce off the router | |
pass in quick on net0 from 172.20.99.0/24 to 172.20.99.0/24 | |
pass out quick on net0 from 172.20.99.0/24 to 172.20.99.0/24 | |
# allow broadcast to come in | |
pass in quick on net0 proto udp from 172.20.99.0/24 to 255.255.255.255 | |
pass in quick on net0 proto udp from 172.20.99.0/24 to 172.20.99.255 | |
pass out quick on net0 proto udp from 172.20.99.0/24 to 172.20.99.255 | |
pass out quick on net0 proto udp from 172.20.99.0/24 to 255.255.255.255 | |
EOF | |
) > /etc/ipf/ipf.conf | |
if [ $TWO_NICS == 1 ] | |
then | |
( cat << EOF | |
# pass all on net1 | |
pass in quick on net1 all | |
pass out quick on net1 all | |
# allow ping | |
pass in quick on net1 proto icmp from any to any | |
pass out quick on net1 proto icmp from any to any | |
# allow internet acess | |
pass out quick on net1 from any to ${NET1_IPv4}/${NET1_IPv4_NETMASK} | |
pass in quick on net1 from ${NET1_IPv4}/${NET1_IPv4_NETMASK} to any | |
pass out quick on net1 from ${NET1_IPv4}/${NET1_IPv4_NETMASK} to any keep state | |
EOF | |
) >> /etc/ipf/ipf.conf | |
fi | |
echo "Writing ipf6.conf" | |
( cat << EOF | |
# | |
# ipf.conf | |
# | |
# IP Filter rules to be loaded during startup | |
# | |
# See ipf(4) manpage for more information on | |
# IP Filter rules syntax. | |
# allow loopback to work | |
pass in quick on lo0 all | |
pass out quick on lo0 all | |
# drop frags | |
block in log quick all with short | |
# default policy | |
block in log on net0 all | |
block out log on net0 all | |
# allow ospf multicast | |
pass in quick on net0 proto ospf from 2001:470:xxxx:2099::/64 to ff02::5 | |
pass in quick on net0 proto ospf from fe80::/10 to ff02::5 | |
pass out quick on net0 proto ospf from 2001:470:xxxx:2099::/64 to ff02::5 | |
pass out quick on net0 proto ospf from fe80::/10 to ff02::5 | |
# and apparently ospf unicast is a thing | |
pass in quick on net0 proto ospf from fe80::/10 to fe80::/10 | |
pass out quick on net0 proto ospf from fe80::/10 to fe80::/10 | |
# allow ipv6-icmp to work on net0 | |
pass in quick on net0 proto ipv6-icmp from any to fe80::/10 | |
pass in quick on net0 proto ipv6-icmp from any to 2001:470:xxxx:2099::/64 | |
pass out quick on net0 proto ipv6-icmp from fe80::/10 to any | |
pass out quick on net0 proto ipv6-icmp from 2001:470:xxxx:2099::/64 to any | |
pass out quick on net0 proto ipv6-icmp from 2001:470:xxxx:245a::/64 to any | |
# allow the multicast groups necessary for ipv6 to work | |
pass in quick on net0 from ff00::/8 to any | |
pass in quick on net0 from any to ff00::/8 | |
pass out quick on net0 from ff00::/8 to any | |
pass out quick on net0 from any to ff00::/8 | |
# pass the other fe80 stuff | |
pass in quick on net0 from fe80::/10 to fe80::/10 | |
pass out quick on net0 from fe80::/10 to fe80::/10 | |
EOF | |
) > /etc/ipf/ipf6.conf | |
if [ $TWO_NICS == 1 ] | |
then | |
( cat << EOF | |
block in log on net1 all | |
block out log on net1 all | |
# permit icmpv6 on net1 | |
pass in quick on net0 proto ipv6-icmp from any to ${NET1_IPv6_NETWORK}/64 | |
pass in quick on net1 proto ipv6-icmp from fe80::/10 to any | |
pass in quick on net1 proto ipv6-icmp from ${NET1_IPv6_NETWORK}/64 to any | |
pass out quick on net1 proto ipv6-icmp from any to fe80::/10 | |
pass out quick on net1 proto ipv6-icmp from any to ${NET1_IPv6_NETWORK}/64 | |
# allow multicast on net1 | |
pass in quick on net1 from ff00::/8 to any | |
pass in quick on net1 from any to ff00::/8 | |
pass out quick on net1 from ff00::/8 to any | |
pass out quick on net1 from any to ff00::/8 | |
# more link-local on net1 | |
pass in quick on net1 from fe80::/10 to fe80::/10 | |
pass out quick on net1 from fe80::/10 to fe80::/10 | |
# allow internet access | |
# allow internet access | |
pass in quick on net1 from ${NET1_IPv6_NETWORK}/64 to any | |
pass out quick on net1 from any to ${NET1_IPv6_NETWORK}/64 | |
pass out quick on net0 from ${NET1_IPv6_NETWORK}/64 to any keep state | |
EOF | |
) >> /etc/ipf/ipf6.conf | |
fi | |
# Enable services | |
for SERVICE in quagga:zebra quagga:ospf quagga:ospf6 isc-dhcpd isc-dhcpd6 ipfilter | |
do | |
echo "Enabling ${SERVICE}..." | |
svcadm enable $SERVICE | |
done | |
# Reboot? | |
read -p "All done, reboot now? [Y/n] " DO_REBOOT | |
if [[ $DO_REBOOT == 'y' || $DO_REBOOT == 'Y' ]] | |
then | |
reboot | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment