From 640b2c7d5b69294846f2ea7deda178c7e00b8633 Mon Sep 17 00:00:00 2001 From: Blake Harnden <32446120+bharnden@users.noreply.github.com> Date: Wed, 22 Jan 2020 16:54:45 -0800 Subject: [PATCH] updated config services to account for files that have a path, basename will be used for template rendering, converted all current utility/security services to config service format --- daemon/core/configservice/base.py | 20 +- .../sercurityservices/services.py | 70 +++++ .../sercurityservices/templates/firewall.sh | 30 +++ .../sercurityservices/templates/ipsec.sh | 114 ++++++++ .../sercurityservices/templates/nat.sh | 14 + .../sercurityservices/templates/vpnclient.sh | 2 +- .../sercurityservices/templates/vpnserver.sh | 147 +++++++++++ .../configservices/serviceutils/services.py | 246 +++++++++++++++++- .../serviceutils/templates/apache2.conf | 102 ++++++++ .../serviceutils/templates/defaultmroute.sh | 4 + .../serviceutils/templates/dhcpd.conf | 22 ++ .../serviceutils/templates/envvars | 10 + .../serviceutils/templates/index.html | 13 + .../serviceutils/templates/pcap.sh | 11 + .../serviceutils/templates/radvd.conf | 19 ++ .../serviceutils/templates/sshd_config | 37 +++ .../serviceutils/templates/startatd.sh | 5 + .../serviceutils/templates/startdhcpclient.sh | 8 + .../serviceutils/templates/startsshd.sh | 6 + .../serviceutils/templates/staticroute.sh | 7 + .../serviceutils/templates/vsftpd.conf | 12 + 21 files changed, 888 insertions(+), 11 deletions(-) create mode 100644 daemon/core/configservices/sercurityservices/templates/firewall.sh create mode 100644 daemon/core/configservices/sercurityservices/templates/ipsec.sh create mode 100644 daemon/core/configservices/sercurityservices/templates/nat.sh create mode 100644 daemon/core/configservices/sercurityservices/templates/vpnserver.sh create mode 100644 daemon/core/configservices/serviceutils/templates/apache2.conf create mode 100644 daemon/core/configservices/serviceutils/templates/defaultmroute.sh create mode 100644 daemon/core/configservices/serviceutils/templates/dhcpd.conf create mode 100644 daemon/core/configservices/serviceutils/templates/envvars create mode 100644 daemon/core/configservices/serviceutils/templates/index.html create mode 100644 daemon/core/configservices/serviceutils/templates/pcap.sh create mode 100644 daemon/core/configservices/serviceutils/templates/radvd.conf create mode 100644 daemon/core/configservices/serviceutils/templates/sshd_config create mode 100644 daemon/core/configservices/serviceutils/templates/startatd.sh create mode 100644 daemon/core/configservices/serviceutils/templates/startdhcpclient.sh create mode 100644 daemon/core/configservices/serviceutils/templates/startsshd.sh create mode 100644 daemon/core/configservices/serviceutils/templates/staticroute.sh create mode 100644 daemon/core/configservices/serviceutils/templates/vsftpd.conf diff --git a/daemon/core/configservice/base.py b/daemon/core/configservice/base.py index 990452cd..fa8f0ad3 100644 --- a/daemon/core/configservice/base.py +++ b/daemon/core/configservice/base.py @@ -139,18 +139,17 @@ class ConfigService(abc.ABC): self.custom_templates[name] = template def get_text(self, name: str) -> str: - raise CoreError( - f"node({self.node.name} service({self.name}) unknown template({name})" - ) + raise CoreError(f"service({self.name}) unknown template({name})") def get_templates(self) -> Dict[str, str]: templates = {} for name in self.files: + basename = pathlib.Path(name).name if name in self.custom_templates: template = self.custom_templates[name] template = inspect.cleandoc(template) - elif self.templates.has_template(name): - template = self.templates.get_template(name).source + elif self.templates.has_template(basename): + template = self.templates.get_template(basename).source else: template = self.get_text(name) template = inspect.cleandoc(template) @@ -160,12 +159,13 @@ class ConfigService(abc.ABC): def create_files(self) -> None: data = self.data() for name in self.files: + basename = pathlib.Path(name).name if name in self.custom_templates: text = self.custom_templates[name] text = inspect.cleandoc(text) self.render_text(name, text, data) - elif self.templates.has_template(name): - self.render_template(name, data) + elif self.templates.has_template(basename): + self.render_template(name, basename, data) else: text = self.get_text(name) text = inspect.cleandoc(text) @@ -233,9 +233,11 @@ class ConfigService(abc.ABC): f"{exceptions.text_error_template().render_unicode()}" ) - def render_template(self, name: str, data: Dict[str, Any] = None) -> None: + def render_template( + self, name: str, basename: str, data: Dict[str, Any] = None + ) -> None: try: - template = self.templates.get_template(name) + template = self.templates.get_template(basename) self._render(name, template, data) except Exception: raise CoreError( diff --git a/daemon/core/configservices/sercurityservices/services.py b/daemon/core/configservices/sercurityservices/services.py index 9c8d416a..cc95265d 100644 --- a/daemon/core/configservices/sercurityservices/services.py +++ b/daemon/core/configservices/sercurityservices/services.py @@ -1,3 +1,5 @@ +from typing import Any, Dict + from core.configservice.base import ConfigService, ConfigServiceMode GROUP_NAME = "Security" @@ -16,3 +18,71 @@ class VpnClient(ConfigService): validation_mode = ConfigServiceMode.BLOCKING default_configs = [] modes = {} + + +class VPNServer(ConfigService): + name = "VPNServer" + group = GROUP_NAME + directories = [] + files = ["vpnserver.sh"] + executables = ["openvpn", "ip", "killall"] + dependencies = [] + startup = ["sh vpnserver.sh"] + validate = ["pidof openvpn"] + shutdown = ["killall openvpn"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + +class IPsec(ConfigService): + name = "IPsec" + group = GROUP_NAME + directories = [] + files = ["ipsec.sh"] + executables = ["racoon", "ip", "setkey", "killall"] + dependencies = [] + startup = ["sh ipsec.sh"] + validate = ["pidof racoon"] + shutdown = ["killall racoon"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + +class Firewall(ConfigService): + name = "Firewall" + group = GROUP_NAME + directories = [] + files = ["firewall.sh"] + executables = ["iptables"] + dependencies = [] + startup = ["sh firewall.sh"] + validate = [] + shutdown = [] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + +class Nat(ConfigService): + name = "NAT" + group = GROUP_NAME + directories = [] + files = ["nat.sh"] + executables = ["iptables"] + dependencies = [] + startup = ["sh nat.sh"] + validate = [] + shutdown = [] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + ifnames = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + ifnames.append(ifc.name) + return dict(ifnames=ifnames) diff --git a/daemon/core/configservices/sercurityservices/templates/firewall.sh b/daemon/core/configservices/sercurityservices/templates/firewall.sh new file mode 100644 index 00000000..a445d133 --- /dev/null +++ b/daemon/core/configservices/sercurityservices/templates/firewall.sh @@ -0,0 +1,30 @@ +# -------- CUSTOMIZATION REQUIRED -------- +# +# Below are sample iptables firewall rules that you can uncomment and edit. +# You can also use ip6tables rules for IPv6. +# + +# start by flushing all firewall rules (so this script may be re-run) +#iptables -F + +# allow traffic related to established connections +#iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + +# allow TCP packets from any source destined for 192.168.1.1 +#iptables -A INPUT -s 0/0 -i eth0 -d 192.168.1.1 -p TCP -j ACCEPT + +# allow OpenVPN server traffic from eth0 +#iptables -A INPUT -p udp --dport 1194 -j ACCEPT +#iptables -A INPUT -i eth0 -j DROP +#iptables -A OUTPUT -p udp --sport 1194 -j ACCEPT +#iptables -A OUTPUT -o eth0 -j DROP + +# allow ICMP ping traffic +#iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT +#iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT + +# allow SSH traffic +#iptables -A -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT + +# drop all other traffic coming in eth0 +#iptables -A INPUT -i eth0 -j DROP diff --git a/daemon/core/configservices/sercurityservices/templates/ipsec.sh b/daemon/core/configservices/sercurityservices/templates/ipsec.sh new file mode 100644 index 00000000..e8fde77e --- /dev/null +++ b/daemon/core/configservices/sercurityservices/templates/ipsec.sh @@ -0,0 +1,114 @@ +# -------- CUSTOMIZATION REQUIRED -------- +# +# The IPsec service builds ESP tunnels between the specified peers using the +# racoon IKEv2 keying daemon. You need to provide keys and the addresses of +# peers, along with subnets to tunnel. + +# directory containing the certificate and key described below +keydir=/etc/core/keys + +# the name used for the "$certname.pem" x509 certificate and +# "$certname.key" RSA private key, which can be generated using openssl +certname=ipsec1 + +# list the public-facing IP addresses, starting with the localhost and followed +# by each tunnel peer, separated with a single space +tunnelhosts="172.16.0.1AND172.16.0.2 172.16.0.1AND172.16.2.1" + +# Define T where i is the index for each tunnel peer host from +# the tunnel_hosts list above (0 is localhost). +# T is a list of IPsec tunnels with peer i, with a local subnet address +# followed by the remote subnet address: +# T="AND AND" +# For example, 172.16.0.0/24 is a local network (behind this node) to be +# tunneled and 172.16.2.0/24 is a remote network (behind peer 1) +T1="172.16.3.0/24AND172.16.5.0/24" +T2="172.16.4.0/24AND172.16.5.0/24 172.16.4.0/24AND172.16.6.0/24" + +# -------- END CUSTOMIZATION -------- + +echo "building config $PWD/ipsec.conf..." +echo "building config $PWD/ipsec.conf..." > $PWD/ipsec.log + +checkip=0 +if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then + echo "WARNING: ip validation disabled because package sipcalc not installed + " >> $PWD/ipsec.log + checkip=1 +fi + +echo "#!/usr/sbin/setkey -f + # Flush the SAD and SPD + flush; + spdflush; + + # Security policies " > $PWD/ipsec.conf +i=0 +for hostpair in $tunnelhosts; do + i=`expr $i + 1` + # parse tunnel host IP + thishost=$${}{hostpair%%AND*} + peerhost=$${}{hostpair##*AND} + if [ $checkip = "0" ] && + [ "$(sipcalc "$thishost" "$peerhost" | grep ERR)" != "" ]; then + echo "ERROR: invalid host address $thishost or $peerhost " >> $PWD/ipsec.log + fi + # parse each tunnel addresses + tunnel_list_var_name=T$i + eval tunnels="$"$tunnel_list_var_name"" + for ttunnel in $tunnels; do + lclnet=$${}{ttunnel%%AND*} + rmtnet=$${}{ttunnel##*AND} + if [ $checkip = "0" ] && + [ "$(sipcalc "$lclnet" "$rmtnet"| grep ERR)" != "" ]; then + echo "ERROR: invalid tunnel address $lclnet and $rmtnet " >> $PWD/ipsec.log + fi + # add tunnel policies + echo " + spdadd $lclnet $rmtnet any -P out ipsec + esp/tunnel/$thishost-$peerhost/require; + spdadd $rmtnet $lclnet any -P in ipsec + esp/tunnel/$peerhost-$thishost/require; " >> $PWD/ipsec.conf + done +done + +echo "building config $PWD/racoon.conf..." +if [ ! -e $keydir\/$certname.key ] || [ ! -e $keydir\/$certname.pem ]; then + echo "ERROR: missing certification files under $keydir $certname.key or $certname.pem " >> $PWD/ipsec.log +fi +echo " + path certificate \"$keydir\"; + listen { + adminsock disabled; + } + remote anonymous + { + exchange_mode main; + certificate_type x509 \"$certname.pem\" \"$certname.key\"; + ca_type x509 \"ca-cert.pem\"; + my_identifier asn1dn; + peers_identifier asn1dn; + + proposal { + encryption_algorithm 3des ; + hash_algorithm sha1; + authentication_method rsasig ; + dh_group modp768; + } + } + sainfo anonymous + { + pfs_group modp768; + lifetime time 1 hour ; + encryption_algorithm 3des, blowfish 448, rijndael ; + authentication_algorithm hmac_sha1, hmac_md5 ; + compression_algorithm deflate ; + } + " > $PWD/racoon.conf + +# the setkey program is required from the ipsec-tools package +echo "running setkey -f $PWD/ipsec.conf..." +setkey -f $PWD/ipsec.conf + +echo "running racoon -d -f $PWD/racoon.conf..." +racoon -d -f $PWD/racoon.conf -l racoon.log diff --git a/daemon/core/configservices/sercurityservices/templates/nat.sh b/daemon/core/configservices/sercurityservices/templates/nat.sh new file mode 100644 index 00000000..80b96a08 --- /dev/null +++ b/daemon/core/configservices/sercurityservices/templates/nat.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# generated by security.py +# NAT out the first interface by default +% for index, ifname in enumerate(ifnames): +% if index == 0: +iptables -t nat -A POSTROUTING -o ${ifname} -j MASQUERADE +iptables -A FORWARD -i ${ifname} -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -i ${ifname} -j DROP +% else: +# iptables -t nat -A POSTROUTING -o ${ifname} -j MASQUERADE +# iptables -A FORWARD -i ${ifname} -m state --state RELATED,ESTABLISHED -j ACCEPT +# iptables -A FORWARD -i ${ifname} -j DROP +% endif +% endfor diff --git a/daemon/core/configservices/sercurityservices/templates/vpnclient.sh b/daemon/core/configservices/sercurityservices/templates/vpnclient.sh index 7d620b46..9e2a5d10 100644 --- a/daemon/core/configservices/sercurityservices/templates/vpnclient.sh +++ b/daemon/core/configservices/sercurityservices/templates/vpnclient.sh @@ -34,7 +34,7 @@ if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \ fi # if necessary, add a static route for reaching the VPN server IP via the IF -vpnservernet=${vpnserver%.*}.0/24 +vpnservernet=$${}{vpnserver%.*}.0/24 if [ "$nexthop" != "" ]; then ip route add $vpnservernet via $nexthop fi diff --git a/daemon/core/configservices/sercurityservices/templates/vpnserver.sh b/daemon/core/configservices/sercurityservices/templates/vpnserver.sh new file mode 100644 index 00000000..c46812e2 --- /dev/null +++ b/daemon/core/configservices/sercurityservices/templates/vpnserver.sh @@ -0,0 +1,147 @@ +# -------- CUSTOMIZATION REQUIRED -------- +# +# The VPNServer service sets up the OpenVPN server for building VPN tunnels +# that allow access via TUN/TAP device to private networks. +# +# note that the IPForward and DefaultRoute services should be enabled + +# directory containing the certificate and key described below, in addition to +# a CA certificate and DH key +keydir=/etc/core/keys + +# the name used for a "$keyname.crt" certificate and "$keyname.key" private key. +keyname=server2 + +# the VPN subnet address from which the client VPN IP (for the TUN/TAP) +# will be allocated +vpnsubnet=10.0.200.0 + +# public IP address of this vpn server (same as VPNClient vpnserver= setting) +vpnserver=10.0.2.10 + +# optional list of private subnets reachable behind this VPN server +# each subnet and next hop is separated by a space +# ", , ..." +privatenets="10.0.11.0,10.0.10.1 10.0.12.0,10.0.10.1" + +# optional list of VPN clients, for statically assigning IP addresses to +# clients; also, an optional client subnet can be specified for adding static +# routes via the client +# Note: VPN addresses x.x.x.0-3 are reserved +# ",, ,, ..." +vpnclients="client1KeyFilename,10.0.200.5,10.0.0.0 client2KeyFilename,," + +# NOTE: you may need to enable the StaticRoutes service on nodes within the +# private subnet, in order to have routes back to the client. +# /sbin/ip ro add /24 via +# /sbin/ip ro add /24 via + +# -------- END CUSTOMIZATION -------- + +echo > $PWD/vpnserver.log +rm -f -r $PWD/ccd + +# validate key and certification files +if [ ! -e $keydir\/$keyname.key ] || [ ! -e $keydir\/$keyname.crt ] \ + || [ ! -e $keydir\/ca.crt ] || [ ! -e $keydir\/dh1024.pem ]; then + echo "ERROR: missing certification or key files under $keydir \ +$keyname.key or $keyname.crt or ca.crt or dh1024.pem" >> $PWD/vpnserver.log +fi + +# validate configuration IP addresses +checkip=0 +if [ "$(dpkg -l | grep " sipcalc ")" = "" ]; then + echo "WARNING: ip validation disabled because package sipcalc not installed\ + " >> $PWD/vpnserver.log + checkip=1 +else + if [ "$(sipcalc "$vpnsubnet" "$vpnserver" | grep ERR)" != "" ]; then + echo "ERROR: invalid vpn subnet or server address \ +$vpnsubnet or $vpnserver " >> $PWD/vpnserver.log + fi +fi + +# create client vpn ip pool file +( +cat << EOF +EOF +)> $PWD/ippool.txt + +# create server.conf file +( +cat << EOF +# openvpn server config +local $vpnserver +server $vpnsubnet 255.255.255.0 +push redirect-gateway def1 +EOF +)> $PWD/server.conf + +# add routes to VPN server private subnets, and push these routes to clients +for privatenet in $privatenets; do + if [ $privatenet != "" ]; then + net=$${}{privatenet%%,*} + nexthop=$${}{privatenet##*,} + if [ $checkip = "0" ] && + [ "$(sipcalc "$net" "$nexthop" | grep ERR)" != "" ]; then + echo "ERROR: invalid vpn server private net address \ +$net or $nexthop " >> $PWD/vpnserver.log + fi + echo push route $net 255.255.255.0 >> $PWD/server.conf + ip ro add $net/24 via $nexthop + ip ro add $vpnsubnet/24 via $nexthop + fi +done + +# allow subnet through this VPN, one route for each client subnet +for client in $vpnclients; do + if [ $client != "" ]; then + cSubnetIP=$${}{client##*,} + cVpnIP=$${}{client#*,} + cVpnIP=$${}{cVpnIP%%,*} + cKeyFilename=$${}{client%%,*} + if [ "$cSubnetIP" != "" ]; then + if [ $checkip = "0" ] && + [ "$(sipcalc "$cSubnetIP" "$cVpnIP" | grep ERR)" != "" ]; then + echo "ERROR: invalid vpn client and subnet address \ +$cSubnetIP or $cVpnIP " >> $PWD/vpnserver.log + fi + echo route $cSubnetIP 255.255.255.0 >> $PWD/server.conf + if ! test -d $PWD/ccd; then + mkdir -p $PWD/ccd + echo client-config-dir $PWD/ccd >> $PWD/server.conf + fi + if test -e $PWD/ccd/$cKeyFilename; then + echo iroute $cSubnetIP 255.255.255.0 >> $PWD/ccd/$cKeyFilename + else + echo iroute $cSubnetIP 255.255.255.0 > $PWD/ccd/$cKeyFilename + fi + fi + if [ "$cVpnIP" != "" ]; then + echo $cKeyFilename,$cVpnIP >> $PWD/ippool.txt + fi + fi +done + +( +cat << EOF +keepalive 10 120 +ca $keydir/ca.crt +cert $keydir/$keyname.crt +key $keydir/$keyname.key +dh $keydir/dh1024.pem +cipher AES-256-CBC +status /var/log/openvpn-status.log +log /var/log/openvpn-server.log +ifconfig-pool-linear +ifconfig-pool-persist $PWD/ippool.txt +port 1194 +proto udp +dev tun +verb 4 +daemon +EOF +)>> $PWD/server.conf + +# start vpn server +openvpn --config server.conf diff --git a/daemon/core/configservices/serviceutils/services.py b/daemon/core/configservices/serviceutils/services.py index 51fb5828..1165b149 100644 --- a/daemon/core/configservices/serviceutils/services.py +++ b/daemon/core/configservices/serviceutils/services.py @@ -8,7 +8,7 @@ from core.configservice.base import ConfigService, ConfigServiceMode GROUP_NAME = "Utility" -class DefaultRoute(ConfigService): +class DefaultRouteService(ConfigService): name = "DefaultRoute" group = GROUP_NAME directories = [] @@ -34,6 +34,61 @@ class DefaultRoute(ConfigService): return dict(addresses=addresses) +class DefaultMulticastRouteService(ConfigService): + name = "DefaultMulticastRoute" + group = GROUP_NAME + directories = [] + files = ["defaultmroute.sh"] + executables = [] + dependencies = [] + startup = ["sh defaultmroute.sh"] + validate = [] + shutdown = [] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + ifname = None + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + ifname = ifc.name + break + return dict(ifname=ifname) + + +class StaticRouteService(ConfigService): + name = "StaticRoute" + group = GROUP_NAME + directories = [] + files = ["staticroute.sh"] + executables = [] + dependencies = [] + startup = ["sh staticroute.sh"] + validate = [] + shutdown = [] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + routes = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + for x in ifc.addrlist: + addr = x.split("/")[0] + if netaddr.valid_ipv6(addr): + dst = "3ffe:4::/64" + else: + dst = "10.9.8.0/24" + net = netaddr.IPNetwork(x) + if net[-2] != net[1]: + routes.append((dst, net[1])) + return dict(routes=routes) + + class IpForwardService(ConfigService): name = "IPForward" group = GROUP_NAME @@ -54,3 +109,192 @@ class IpForwardService(ConfigService): devname = utils.sysctl_devname(ifc.name) devnames.append(devname) return dict(devnames=devnames) + + +class SshService(ConfigService): + name = "SSH" + group = GROUP_NAME + directories = ["/etc/ssh", "/var/run/sshd"] + files = ["startsshd.sh", "/etc/ssh/sshd_config"] + executables = ["sshd"] + dependencies = [] + startup = ["sh startsshd.sh"] + validate = [] + shutdown = ["killall sshd"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + return dict( + sshcfgdir=self.directories[0], + sshstatedir=self.directories[1], + sshlibdir="/usr/lib/openssh", + ) + + +class DhcpService(ConfigService): + name = "DHCP" + group = GROUP_NAME + directories = ["/etc/dhcp", "/var/lib/dhcp"] + files = ["/etc/dhcp/dhcpd.conf"] + executables = ["dhcpd"] + dependencies = [] + startup = ["touch /var/lib/dhcp/dhcpd.leases", "dhcpd"] + validate = ["pidof dhcpd"] + shutdown = ["killall dhcpd"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + subnets = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + for x in ifc.addrlist: + addr = x.split("/")[0] + if netaddr.valid_ipv4(addr): + net = netaddr.IPNetwork(x) + # divide the address space in half + index = (net.size - 2) / 2 + rangelow = net[index] + rangehigh = net[-2] + subnets.append((net.ip, net.netmask, rangelow, rangehigh, addr)) + return dict(subnets=subnets) + + +class DhcpClientService(ConfigService): + name = "DHCPClient" + group = GROUP_NAME + directories = [] + files = ["startdhcpclient.sh"] + executables = ["dhclient"] + dependencies = [] + startup = ["sh startdhcpclient.sh"] + validate = ["pidof dhclient"] + shutdown = ["killall dhclient"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + ifnames = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + ifnames.append(ifc.name) + return dict(ifnames=ifnames) + + +class FtpService(ConfigService): + name = "FTP" + group = GROUP_NAME + directories = ["/var/run/vsftpd/empty", "/var/ftp"] + files = ["vsftpd.conf"] + executables = ["vsftpd"] + dependencies = [] + startup = ["vsftpd ./vsftpd.conf"] + validate = ["pidof vsftpd"] + shutdown = ["killall vsftpd"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + +class PcapService(ConfigService): + name = "pcap" + group = GROUP_NAME + directories = [] + files = ["pcap.sh"] + executables = ["tcpdump"] + dependencies = [] + startup = ["sh pcap.sh start"] + validate = ["pidof tcpdump"] + shutdown = ["sh pcap.sh stop"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + ifnames = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + ifnames.append(ifc.name) + return dict() + + +class RadvdService(ConfigService): + name = "radvd" + group = GROUP_NAME + directories = ["/etc/radvd"] + files = ["/etc/radvd/radvd.conf"] + executables = ["radvd"] + dependencies = [] + startup = ["radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log"] + validate = ["pidof radvd"] + shutdown = ["pkill radvd"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + interfaces = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + prefixes = [] + for x in ifc.addrlist: + addr = x.split("/")[0] + if netaddr.valid_ipv6(addr): + prefixes.append(x) + if not prefixes: + continue + interfaces.append((ifc.name, prefixes)) + return dict(interfaces=interfaces) + + +class AtdService(ConfigService): + name = "atd" + group = GROUP_NAME + directories = ["/var/spool/cron/atjobs", "/var/spool/cron/atspool"] + files = ["startatd.sh"] + executables = ["atd"] + dependencies = [] + startup = ["sh startatd.sh"] + validate = ["pidof atd"] + shutdown = ["pkill atd"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + +class HttpService(ConfigService): + name = "HTTP" + group = GROUP_NAME + directories = [ + "/etc/apache2", + "/var/run/apache2", + "/var/log/apache2", + "/run/lock", + "/var/lock/apache2", + "/var/www", + ] + files = ["/etc/apache2/apache2.conf", "/etc/apache2/envvars", "/var/www/index.html"] + executables = ["apache2ctl"] + dependencies = [] + startup = ["chown www-data /var/lock/apache2", "apache2ctl start"] + validate = ["pidof apache2"] + shutdown = ["apache2ctl stop"] + validation_mode = ConfigServiceMode.BLOCKING + default_configs = [] + modes = {} + + def data(self) -> Dict[str, Any]: + interfaces = [] + for ifc in self.node.netifs(): + if hasattr(ifc, "control") and ifc.control is True: + continue + interfaces.append(ifc) + return dict(interfaces=interfaces) diff --git a/daemon/core/configservices/serviceutils/templates/apache2.conf b/daemon/core/configservices/serviceutils/templates/apache2.conf new file mode 100644 index 00000000..c53e48af --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/apache2.conf @@ -0,0 +1,102 @@ +# apache2.conf generated by utility.py:HttpService +Mutex file:$APACHE_LOCK_DIR default + +PidFile $APACHE_PID_FILE +Timeout 300 +KeepAlive On +MaxKeepAliveRequests 100 +KeepAliveTimeout 5 + +LoadModule mpm_worker_module /usr/lib/apache2/modules/mod_mpm_worker.so + + + StartServers 5 + MinSpareServers 5 + MaxSpareServers 10 + MaxClients 150 + MaxRequestsPerChild 0 + + + + StartServers 2 + MinSpareThreads 25 + MaxSpareThreads 75 + ThreadLimit 64 + ThreadsPerChild 25 + MaxClients 150 + MaxRequestsPerChild 0 + + + + StartServers 2 + MinSpareThreads 25 + MaxSpareThreads 75 + ThreadLimit 64 + ThreadsPerChild 25 + MaxClients 150 + MaxRequestsPerChild 0 + + +User $APACHE_RUN_USER +Group $APACHE_RUN_GROUP + +AccessFileName .htaccess + + + Require all denied + + +DefaultType None + +HostnameLookups Off + +ErrorLog $APACHE_LOG_DIR/error.log +LogLevel warn + +#Include mods-enabled/*.load +#Include mods-enabled/*.conf +LoadModule alias_module /usr/lib/apache2/modules/mod_alias.so +LoadModule auth_basic_module /usr/lib/apache2/modules/mod_auth_basic.so +LoadModule authz_core_module /usr/lib/apache2/modules/mod_authz_core.so +LoadModule authz_host_module /usr/lib/apache2/modules/mod_authz_host.so +LoadModule authz_user_module /usr/lib/apache2/modules/mod_authz_user.so +LoadModule autoindex_module /usr/lib/apache2/modules/mod_autoindex.so +LoadModule dir_module /usr/lib/apache2/modules/mod_dir.so +LoadModule env_module /usr/lib/apache2/modules/mod_env.so + +NameVirtualHost *:80 +Listen 80 + + + Listen 443 + + + Listen 443 + + +LogFormat "%v:%p %h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" vhost_combined +LogFormat "%h %l %u %t \\"%r\\" %>s %O \\"%{Referer}i\\" \\"%{User-Agent}i\\"" combined +LogFormat "%h %l %u %t \\"%r\\" %>s %O" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +ServerTokens OS +ServerSignature On +TraceEnable Off + + + ServerAdmin webmaster@localhost + DocumentRoot /var/www + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Require all granted + + ErrorLog $APACHE_LOG_DIR/error.log + LogLevel warn + CustomLog $APACHE_LOG_DIR/access.log combined + diff --git a/daemon/core/configservices/serviceutils/templates/defaultmroute.sh b/daemon/core/configservices/serviceutils/templates/defaultmroute.sh new file mode 100644 index 00000000..4a8d9403 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/defaultmroute.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# auto-generated by DefaultMulticastRoute service (utility.py) +# the first interface is chosen below; please change it as needed +ip route add 224.0.0.0/4 dev ${ifname} diff --git a/daemon/core/configservices/serviceutils/templates/dhcpd.conf b/daemon/core/configservices/serviceutils/templates/dhcpd.conf new file mode 100644 index 00000000..7be7f4e8 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/dhcpd.conf @@ -0,0 +1,22 @@ +# auto-generated by DHCP service (utility.py) +# NOTE: move these option lines into the desired pool { } block(s) below +#option domain-name "test.com"; +#option domain-name-servers 10.0.0.1; +#option routers 10.0.0.1; + +log-facility local6; + +default-lease-time 600; +max-lease-time 7200; + +ddns-update-style none; + +% for subnet, netmask, rangelow, rangehigh, addr in subnets: +subnet ${subnet} netmask ${netmask} { + pool { + range ${rangelow} ${rangehigh}; + default-lease-time 600; + option routers ${addr}; + } +} +% endfor diff --git a/daemon/core/configservices/serviceutils/templates/envvars b/daemon/core/configservices/serviceutils/templates/envvars new file mode 100644 index 00000000..fcfc4d9e --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/envvars @@ -0,0 +1,10 @@ +# this file is used by apache2ctl - generated by utility.py:HttpService +# these settings come from a default Ubuntu apache2 installation +export APACHE_RUN_USER=www-data +export APACHE_RUN_GROUP=www-data +export APACHE_PID_FILE=/var/run/apache2.pid +export APACHE_RUN_DIR=/var/run/apache2 +export APACHE_LOCK_DIR=/var/lock/apache2 +export APACHE_LOG_DIR=/var/log/apache2 +export LANG=C +export LANG diff --git a/daemon/core/configservices/serviceutils/templates/index.html b/daemon/core/configservices/serviceutils/templates/index.html new file mode 100644 index 00000000..aaf9d9fa --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/index.html @@ -0,0 +1,13 @@ + + + +

${node.name} web server

+

This is the default web page for this server.

+

The web server software is running but no content has been added, yet.

+
    +% for ifc in interfaces: +
  • ${ifc.name} - ${ifc.addrlist}
  • +% endfor +
+ + diff --git a/daemon/core/configservices/serviceutils/templates/pcap.sh b/daemon/core/configservices/serviceutils/templates/pcap.sh new file mode 100644 index 00000000..6a099f8c --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/pcap.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# set tcpdump options here (see 'man tcpdump' for help) +# (-s snap length, -C limit pcap file length, -n disable name resolution) +if [ "x$1" = "xstart" ]; then +% for ifname in ifnames: + tcpdump -s 12288 -C 10 -n -w ${node.name}.${ifname}.pcap -i ${ifname} < /dev/null & +% endfor +elif [ "x$1" = "xstop" ]; then + mkdir -p $SESSION_DIR/pcap + mv *.pcap $SESSION_DIR/pcap +fi; diff --git a/daemon/core/configservices/serviceutils/templates/radvd.conf b/daemon/core/configservices/serviceutils/templates/radvd.conf new file mode 100644 index 00000000..1436f068 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/radvd.conf @@ -0,0 +1,19 @@ +# auto-generated by RADVD service (utility.py) +% for ifname, prefixes in values: +interface ${ifname} +{ + AdvSendAdvert on; + MinRtrAdvInterval 3; + MaxRtrAdvInterval 10; + AdvDefaultPreference low; + AdvHomeAgentFlag off; +% for prefix in prefixes: + prefix ${prefix} + { + AdvOnLink on; + AdvAutonomous on; + AdvRouterAddr on; + }; +% endfor +}; +% endfor diff --git a/daemon/core/configservices/serviceutils/templates/sshd_config b/daemon/core/configservices/serviceutils/templates/sshd_config new file mode 100644 index 00000000..826dd098 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/sshd_config @@ -0,0 +1,37 @@ +# auto-generated by SSH service (utility.py) +Port 22 +Protocol 2 +HostKey ${sshcfgdir}/ssh_host_rsa_key +UsePrivilegeSeparation yes +PidFile ${sshstatedir}/sshd.pid + +KeyRegenerationInterval 3600 +ServerKeyBits 768 + +SyslogFacility AUTH +LogLevel INFO + +LoginGraceTime 120 +PermitRootLogin yes +StrictModes yes + +RSAAuthentication yes +PubkeyAuthentication yes + +IgnoreRhosts yes +RhostsRSAAuthentication no +HostbasedAuthentication no + +PermitEmptyPasswords no +ChallengeResponseAuthentication no + +X11Forwarding yes +X11DisplayOffset 10 +PrintMotd no +PrintLastLog yes +TCPKeepAlive yes + +AcceptEnv LANG LC_* +Subsystem sftp ${sshlibdir}/sftp-server +UsePAM yes +UseDNS no diff --git a/daemon/core/configservices/serviceutils/templates/startatd.sh b/daemon/core/configservices/serviceutils/templates/startatd.sh new file mode 100644 index 00000000..6d9d2949 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/startatd.sh @@ -0,0 +1,5 @@ +#!/bin/sh +echo 00001 > /var/spool/cron/atjobs/.SEQ +chown -R daemon /var/spool/cron/* +chmod -R 700 /var/spool/cron/* +atd diff --git a/daemon/core/configservices/serviceutils/templates/startdhcpclient.sh b/daemon/core/configservices/serviceutils/templates/startdhcpclient.sh new file mode 100644 index 00000000..061e66d7 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/startdhcpclient.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# auto-generated by DHCPClient service (utility.py) +# uncomment this mkdir line and symlink line to enable client-side DNS\n# resolution based on the DHCP server response. +#mkdir -p /var/run/resolvconf/interface +% for ifname in ifnames: +#ln -s /var/run/resolvconf/interface/${ifname}.dhclient /var/run/resolvconf/resolv.conf +dhclient -nw -pf /var/run/dhclient-${ifname}.pid -lf /var/run/dhclient-${ifname}.lease ${ifname} +% endfor diff --git a/daemon/core/configservices/serviceutils/templates/startsshd.sh b/daemon/core/configservices/serviceutils/templates/startsshd.sh new file mode 100644 index 00000000..b35fdb07 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/startsshd.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# auto-generated by SSH service (utility.py) +ssh-keygen -q -t rsa -N "" -f ${sshcfgdir}/ssh_host_rsa_key +chmod 655 ${sshstatedir} +# wait until RSA host key has been generated to launch sshd +$(which sshd) -f ${sshcfgdir}/sshd_config diff --git a/daemon/core/configservices/serviceutils/templates/staticroute.sh b/daemon/core/configservices/serviceutils/templates/staticroute.sh new file mode 100644 index 00000000..c47c09fd --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/staticroute.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# auto-generated by StaticRoute service (utility.py) +# NOTE: this service must be customized to be of any use +# Below are samples that you can uncomment and edit. +% for dest, addr in routes: +#ip route add ${dest} via ${addr} +% endfor diff --git a/daemon/core/configservices/serviceutils/templates/vsftpd.conf b/daemon/core/configservices/serviceutils/templates/vsftpd.conf new file mode 100644 index 00000000..988b8727 --- /dev/null +++ b/daemon/core/configservices/serviceutils/templates/vsftpd.conf @@ -0,0 +1,12 @@ +# vsftpd.conf auto-generated by FTP service (utility.py) +listen=YES +anonymous_enable=YES +local_enable=YES +dirmessage_enable=YES +use_localtime=YES +xferlog_enable=YES +connect_from_port_20=YES +xferlog_file=/var/log/vsftpd.log +ftpd_banner=Welcome to the CORE FTP service +secure_chroot_dir=/var/run/vsftpd/empty +anon_root=/var/ftp