summaryrefslogtreecommitdiff
path: root/data/templates
diff options
context:
space:
mode:
Diffstat (limited to 'data/templates')
-rw-r--r--data/templates/accel-ppp/chap-secrets.ipoe.tmpl18
-rw-r--r--data/templates/accel-ppp/chap-secrets.tmpl10
-rw-r--r--data/templates/accel-ppp/ipoe.config.tmpl111
-rw-r--r--data/templates/accel-ppp/l2tp.config.tmpl148
-rw-r--r--data/templates/accel-ppp/pppoe.config.tmpl204
-rw-r--r--data/templates/accel-ppp/pptp.config.tmpl89
-rw-r--r--data/templates/accel-ppp/sstp.config.tmpl146
-rw-r--r--data/templates/bcast-relay/udp-broadcast-relay.tmpl7
-rw-r--r--data/templates/conserver/conserver.conf.tmpl37
-rw-r--r--data/templates/dhcp-client/daemon-options.tmpl4
-rw-r--r--data/templates/dhcp-client/ipv4.tmpl19
-rw-r--r--data/templates/dhcp-client/ipv6.tmpl57
-rw-r--r--data/templates/dhcp-relay/config.tmpl4
-rw-r--r--data/templates/dhcp-server/dhcpd.conf.tmpl195
-rw-r--r--data/templates/dhcpv6-relay/config.tmpl4
-rw-r--r--data/templates/dhcpv6-server/dhcpdv6.conf.tmpl81
-rw-r--r--data/templates/dns-forwarding/recursor.conf.lua.tmpl9
-rw-r--r--data/templates/dns-forwarding/recursor.conf.tmpl33
-rw-r--r--data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl28
-rw-r--r--data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl24
-rw-r--r--data/templates/dynamic-dns/ddclient.conf.tmpl46
-rw-r--r--data/templates/firewall/nftables-nat.tmpl157
-rw-r--r--data/templates/frr/bfd.frr.tmpl16
-rw-r--r--data/templates/frr/bgp.frr.tmpl1
-rw-r--r--data/templates/frr/igmp.frr.tmpl41
-rw-r--r--data/templates/frr/ldpd.frr.tmpl70
-rw-r--r--data/templates/frr/pimd.frr.tmpl34
-rw-r--r--data/templates/frr/rip.frr.tmpl143
-rw-r--r--data/templates/frr/static_mcast.frr.tmpl20
-rw-r--r--data/templates/getty/serial-getty.service.tmpl37
-rw-r--r--data/templates/https/nginx.default.tmpl69
-rw-r--r--data/templates/ids/fastnetmon.tmpl60
-rw-r--r--data/templates/ids/fastnetmon_networks_list.tmpl7
-rw-r--r--data/templates/igmp-proxy/igmpproxy.conf.tmpl37
-rw-r--r--data/templates/ipsec/charon.tmpl342
-rw-r--r--data/templates/ipsec/ipsec.conf.tmpl3
-rw-r--r--data/templates/ipsec/ipsec.secrets.tmpl7
-rw-r--r--data/templates/ipsec/remote-access.tmpl28
-rw-r--r--data/templates/lcd/LCDd.conf.tmpl132
-rw-r--r--data/templates/lcd/lcdproc.conf.tmpl60
-rw-r--r--data/templates/lldp/lldpd.tmpl3
-rw-r--r--data/templates/lldp/vyos.conf.tmpl20
-rw-r--r--data/templates/macsec/wpa_supplicant.conf.tmpl89
-rw-r--r--data/templates/mdns-repeater/mdns-repeater.tmpl2
-rw-r--r--data/templates/netflow/uacctd.conf.tmpl69
-rw-r--r--data/templates/ntp/ntp.conf.tmpl47
-rw-r--r--data/templates/ntp/override.conf.tmpl11
-rw-r--r--data/templates/ocserv/ocserv_config.tmpl82
-rw-r--r--data/templates/ocserv/ocserv_passwd.tmpl6
-rw-r--r--data/templates/ocserv/radius_conf.tmpl22
-rw-r--r--data/templates/ocserv/radius_servers.tmpl7
-rw-r--r--data/templates/openvpn/client.conf.tmpl35
-rw-r--r--data/templates/openvpn/server.conf.tmpl262
-rw-r--r--data/templates/pppoe/ip-down.script.tmpl36
-rw-r--r--data/templates/pppoe/ip-pre-up.script.tmpl18
-rw-r--r--data/templates/pppoe/ip-up.script.tmpl49
-rw-r--r--data/templates/pppoe/ipv6-up.script.tmpl83
-rw-r--r--data/templates/pppoe/peer.tmpl76
-rw-r--r--data/templates/router-advert/radvd.conf.tmpl47
-rw-r--r--data/templates/rsyslog/rsyslog.conf59
-rw-r--r--data/templates/salt-minion/minion.tmpl59
-rw-r--r--data/templates/snmp/etc.snmp.conf.tmpl4
-rw-r--r--data/templates/snmp/etc.snmpd.conf.tmpl115
-rw-r--r--data/templates/snmp/override.conf.tmpl13
-rw-r--r--data/templates/snmp/usr.snmpd.conf.tmpl6
-rw-r--r--data/templates/snmp/var.snmpd.conf.tmpl14
-rw-r--r--data/templates/ssh/override.conf.tmpl11
-rw-r--r--data/templates/ssh/sshd_config.tmpl114
-rw-r--r--data/templates/syslog/logrotate.tmpl12
-rw-r--r--data/templates/syslog/rsyslog.conf.tmpl44
-rw-r--r--data/templates/system-login/pam_radius_auth.conf.tmpl16
-rw-r--r--data/templates/system/curlrc.tmpl8
-rw-r--r--data/templates/system/ssh_config.tmpl3
-rw-r--r--data/templates/tftp-server/default.tmpl2
-rw-r--r--data/templates/vrf/vrf.conf.tmpl8
-rw-r--r--data/templates/vrrp/daemon.tmpl5
-rw-r--r--data/templates/vrrp/keepalived.conf.tmpl97
-rw-r--r--data/templates/vyos-hostsd/hosts.tmpl26
-rw-r--r--data/templates/vyos-hostsd/resolv.conf.tmpl26
-rw-r--r--data/templates/wifi/cfg80211.conf.tmpl1
-rw-r--r--data/templates/wifi/crda.tmpl1
-rw-r--r--data/templates/wifi/hostapd.conf.tmpl687
-rw-r--r--data/templates/wifi/wpa_supplicant.conf.tmpl9
-rw-r--r--data/templates/wwan/chat.tmpl6
-rw-r--r--data/templates/wwan/ip-down.script.tmpl27
-rw-r--r--data/templates/wwan/ip-pre-up.script.tmpl23
-rw-r--r--data/templates/wwan/ip-up.script.tmpl25
-rw-r--r--data/templates/wwan/peer.tmpl27
88 files changed, 4950 insertions, 0 deletions
diff --git a/data/templates/accel-ppp/chap-secrets.ipoe.tmpl b/data/templates/accel-ppp/chap-secrets.ipoe.tmpl
new file mode 100644
index 000000000..a7d899354
--- /dev/null
+++ b/data/templates/accel-ppp/chap-secrets.ipoe.tmpl
@@ -0,0 +1,18 @@
+# username server password acceptable local IP addresses shaper
+{% for interface in auth_interfaces -%}
+{% for mac in interface.mac -%}
+{% if mac.rate_upload and mac.rate_download -%}
+{% if mac.vlan_id -%}
+{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
+{% else -%}
+{{ interface.name }} * {{ mac.address | lower }} * {{ mac.rate_download }}/{{ mac.rate_upload }}
+{% endif -%}
+{% else -%}
+{% if mac.vlan_id -%}
+{{ interface.name }}.{{ mac.vlan_id }} * {{ mac.address | lower }} *
+{% else -%}
+{{ interface.name }} * {{ mac.address | lower }} *
+{% endif -%}
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
diff --git a/data/templates/accel-ppp/chap-secrets.tmpl b/data/templates/accel-ppp/chap-secrets.tmpl
new file mode 100644
index 000000000..dd00d7bd0
--- /dev/null
+++ b/data/templates/accel-ppp/chap-secrets.tmpl
@@ -0,0 +1,10 @@
+# username server password acceptable local IP addresses shaper
+{% for user in local_users %}
+{% if user.state == 'enabled' %}
+{% if user.upload and user.download %}
+{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }} {{ user.download }} / {{ user.upload }}
+{% else %}
+{{ "%-12s" | format(user.name) }} * {{ "%-16s" | format(user.password) }} {{ "%-16s" | format(user.ip) }}
+{% endif %}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/accel-ppp/ipoe.config.tmpl b/data/templates/accel-ppp/ipoe.config.tmpl
new file mode 100644
index 000000000..fca520efa
--- /dev/null
+++ b/data/templates/accel-ppp/ipoe.config.tmpl
@@ -0,0 +1,111 @@
+### generated by ipoe.py ###
+[modules]
+log_syslog
+ipoe
+shaper
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+ippool
+{% if auth_mode == 'radius' %}
+radius
+{% elif auth_mode == 'local' %}
+chap-secrets
+{% endif %}
+
+[core]
+thread-count={{ thread_cnt }}
+
+[log]
+syslog=accel-ipoe,daemon
+copy=1
+level=5
+
+[ipoe]
+verbose=1
+{% for interface in interfaces %}
+{% if interface.vlan_mon %}
+interface=re:{{ interface.name }}\.\d+,{% else %}interface={{ interface.name }},{% endif %}shared={{ interface.shared }},mode={{ interface.mode }},ifcfg={{ interface.ifcfg }},range={{ interface.range }},start={{ interface.sess_start }},ipv6=1
+{% endfor %}
+{% if auth_mode == 'noauth' %}
+noauth=1
+{% elif auth_mode == 'local' %}
+username=ifname
+password=csid
+{% endif %}
+
+{%- for interface in interfaces %}
+{% if (interface.shared == '0') and (interface.vlan_mon) %}
+vlan-mon={{ interface.name }},{{ interface.vlan_mon | join(',') }}
+{% endif %}
+{% endfor %}
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for dns in dnsv6 -%}
+{{ dns }}
+{% endfor -%}
+{% endif %}
+
+[ipv6-nd]
+verbose=1
+
+[ipv6-dhcp]
+verbose=1
+
+{% if client_ipv6_pool %}
+[ipv6-pool]
+{% for p in client_ipv6_pool %}
+{{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% for p in client_ipv6_delegate_prefix %}
+delegate={{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% endif %}
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
+{% endfor -%}
+
+acct-timeout={{ radius_acct_tmo }}
+timeout={{ radius_timeout }}
+max-try={{ radius_max_try }}
+{% if radius_nas_id %}
+nas-identifier={{ radius_nas_id }}
+{% endif -%}
+{% if radius_nas_ip %}
+nas-ip-address={{ radius_nas_ip }}
+{% endif -%}
+{% if radius_source_address %}
+bind={{ radius_source_address }}
+{% endif -%}
+
+{% if radius_dynamic_author %}
+dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
+{% endif -%}
+
+{% if radius_shaper_attr %}
+[shaper]
+verbose=1
+attr={{ radius_shaper_attr }}
+{% if radius_shaper_vendor %}
+vendor={{ radius_shaper_vendor }}
+{% endif -%}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2002
diff --git a/data/templates/accel-ppp/l2tp.config.tmpl b/data/templates/accel-ppp/l2tp.config.tmpl
new file mode 100644
index 000000000..b9131684d
--- /dev/null
+++ b/data/templates/accel-ppp/l2tp.config.tmpl
@@ -0,0 +1,148 @@
+### generated by accel_l2tp.py ###
+[modules]
+log_syslog
+l2tp
+chap-secrets
+{% for proto in auth_proto: %}
+{{proto}}
+{% endfor%}
+
+{% if auth_mode == 'radius' %}
+radius
+{% endif -%}
+
+ippool
+shaper
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+
+[core]
+thread-count={{thread_cnt}}
+
+[log]
+syslog=accel-l2tp,daemon
+copy=1
+level=5
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for dns in dnsv6 -%}
+{{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% for server in wins -%}
+wins{{ loop.index }}={{ server }}
+{% endfor -%}
+{% endif %}
+
+[l2tp]
+verbose=1
+ifname=l2tp%d
+ppp-max-mtu={{ mtu }}
+mppe={{ ppp_mppe }}
+{% if outside_addr %}
+bind={{ outside_addr }}
+{% endif %}
+{% if lns_shared_secret %}
+secret={{ lns_shared_secret }}
+{% endif %}
+
+[client-ip-range]
+0.0.0.0/0
+
+{% if client_ip_pool or client_ip_subnets %}
+[ip-pool]
+{% if client_ip_pool %}
+{{ client_ip_pool }}
+{% endif -%}
+{% if client_ip_subnets %}
+{% for sn in client_ip_subnets %}
+{{sn}}
+{% endfor -%}
+{% endif %}
+{% endif %}
+{% if gateway_address %}
+gw-ip-address={{ gateway_address }}
+{% endif %}
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
+{% endfor -%}
+
+acct-timeout={{ radius_acct_tmo }}
+timeout={{ radius_timeout }}
+max-try={{ radius_max_try }}
+
+{% if radius_nas_id %}
+nas-identifier={{ radius_nas_id }}
+{% endif -%}
+{% if radius_nas_ip %}
+nas-ip-address={{ radius_nas_ip }}
+{% endif -%}
+{% if radius_source_address %}
+bind={{ radius_source_address }}
+{% endif -%}
+{% endif %}
+{% if gateway_address %}
+gw-ip-address={{ gateway_address }}
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+single-session=replace
+lcp-echo-timeout={{ ppp_echo_timeout }}
+lcp-echo-interval={{ ppp_echo_interval }}
+lcp-echo-failure={{ ppp_echo_failure }}
+{% if ccp_disable %}
+ccp=0
+{% endif %}
+{% if client_ipv6_pool %}
+ipv6=allow
+{% endif %}
+
+
+{% if client_ipv6_pool %}
+[ipv6-pool]
+{% for p in client_ipv6_pool %}
+{{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% for p in client_ipv6_delegate_prefix %}
+delegate={{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% endif %}
+
+{% if client_ipv6_delegate_prefix %}
+[ipv6-dhcp]
+verbose=1
+{% endif %}
+
+{% if radius_shaper_attr %}
+[shaper]
+verbose=1
+attr={{ radius_shaper_attr }}
+{% if radius_shaper_vendor %}
+vendor={{ radius_shaper_vendor }}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2004
+sessions-columns=ifname,username,calling-sid,ip,{{ ip6_column | join(',') }}{{ ',' if ip6_column }}rate-limit,type,comp,state,rx-bytes,tx-bytes,uptime
diff --git a/data/templates/accel-ppp/pppoe.config.tmpl b/data/templates/accel-ppp/pppoe.config.tmpl
new file mode 100644
index 000000000..5ad628fde
--- /dev/null
+++ b/data/templates/accel-ppp/pppoe.config.tmpl
@@ -0,0 +1,204 @@
+### generated by accel_pppoe.py ###
+[modules]
+log_syslog
+pppoe
+{% if auth_mode == 'radius' %}
+radius
+{% endif %}
+chap-secrets
+ippool
+{% if ppp_ipv6 != 'deny' %}
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+{% endif %}
+{% for proto in auth_proto: %}
+{{proto}}
+{% endfor%}
+shaper
+{% if snmp %}
+net-snmp
+{% endif %}
+{% if limits %}
+connlimit
+{% endif %}
+
+[core]
+thread-count={{ thread_cnt }}
+
+[log]
+syslog=accel-pppoe,daemon
+copy=1
+level=5
+
+{% if snmp == 'enable-ma' %}
+[snmp]
+master=1
+{% endif %}
+
+[client-ip-range]
+disable
+
+{% if ppp_gw %}
+[ip-pool]
+gw-ip-address={{ ppp_gw }}
+{% if client_ip_pool %}
+{{ client_ip_pool }}
+{% endif -%}
+{% if client_ip_subnets %}
+{% for subnet in client_ip_subnets %}
+{{ subnet }}
+{% endfor %}
+{% endif %}
+{% endif %}
+
+{% if client_ipv6_pool %}
+[ipv6-nd]
+AdvAutonomousFlag=1
+
+[ipv6-pool]
+{% for p in client_ipv6_pool %}
+{{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% for p in client_ipv6_delegate_prefix %}
+delegate={{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% endif %}
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for dns in dnsv6 -%}
+{{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% for server in wins -%}
+wins{{ loop.index }}={{ server }}
+{% endfor -%}
+{% endif %}
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
+{% endfor -%}
+
+acct-timeout={{ radius_acct_tmo }}
+timeout={{ radius_timeout }}
+max-try={{ radius_max_try }}
+
+{% if radius_nas_id %}
+nas-identifier={{ radius_nas_id }}
+{% endif -%}
+{% if radius_nas_ip %}
+nas-ip-address={{ radius_nas_ip }}
+{% endif -%}
+{% if radius_source_address %}
+bind={{ radius_source_address }}
+{% endif -%}
+
+
+{% if radius_dynamic_author %}
+dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
+{% endif -%}
+{% endif %}
+{% if ppp_gw %}
+gw-ip-address={{ ppp_gw }}
+{% endif %}
+
+{% if sesscrtl != 'disable' %}
+[common]
+single-session={{ sesscrtl }}
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+{% if ppp_ccp %}
+ccp=1
+{% else %}
+ccp=0
+{% endif %}
+{% if ppp_min_mtu %}
+min-mtu={{ ppp_min_mtu }}
+{% else %}
+min-mtu={{ mtu }}
+{% endif %}
+{% if ppp_mru %}
+mru={{ ppp_mru }}
+{% endif %}
+mppe={{ ppp_mppe }}
+lcp-echo-interval={{ ppp_echo_interval }}
+lcp-echo-timeout={{ ppp_echo_timeout }}
+lcp-echo-failure={{ ppp_echo_failure }}
+{% if ppp_ipv4 %}
+ipv4={{ ppp_ipv4 }}
+{% endif %}
+{% if client_ipv6_pool %}
+ipv6=allow
+{% endif %}
+
+{% if ppp_ipv6 %}
+ipv6={{ ppp_ipv6 }}
+{% if ppp_ipv6_intf_id %}
+ipv6-intf-id={{ ppp_ipv6_intf_id }}
+{% endif %}
+{% if ppp_ipv6_peer_intf_id %}
+ipv6-peer-intf-id={{ ppp_ipv6_peer_intf_id }}
+{% endif %}
+{% if ppp_ipv6_accept_peer_intf_id %}
+ipv6-accept-peer-intf-id={{ ppp_ipv6_accept_peer_intf_id }}
+{% endif %}
+{% endif %}
+mtu={{ mtu }}
+
+[pppoe]
+verbose=1
+ac-name={{ concentrator }}
+
+{% if interfaces %}
+{% for interface in interfaces %}
+interface={{ interface.name }}
+{% if interface.vlans %}
+vlan-mon={{ interface.name }},{{ interface.vlans | join(',') }}
+interface=re:{{ interface.name }}\.\d+
+{% endif %}
+{% endfor -%}
+{% endif -%}
+
+{% if svc_name %}
+service-name={{ svc_name|join(',') }}
+{% endif -%}
+
+{% if pado_delay %}
+pado-delay={{ pado_delay }}
+{% endif %}
+
+{% if limits_burst or limits_connections or limits_connections %}
+[connlimit]
+{% if limits_connections %}
+limit={{ limits_connections }}
+{% endif %}
+{% if limits_burst %}
+burst={{ limits_burst }}
+{% endif %}
+{% if limits_timeout %}
+timeout={{ limits_timeout }}
+{% endif %}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2001
diff --git a/data/templates/accel-ppp/pptp.config.tmpl b/data/templates/accel-ppp/pptp.config.tmpl
new file mode 100644
index 000000000..e0f2c6da9
--- /dev/null
+++ b/data/templates/accel-ppp/pptp.config.tmpl
@@ -0,0 +1,89 @@
+### generated by accel_pptp.py ###
+[modules]
+log_syslog
+pptp
+ippool
+{% if auth_mode == 'local' %}
+chap-secrets
+{% elif auth_mode == 'radius' %}
+radius
+{% endif -%}
+{% for proto in auth_proto %}
+{{proto}}
+{% endfor %}
+
+[core]
+thread-count={{ thread_cnt }}
+
+[log]
+syslog=accel-pptp,daemon
+copy=1
+level=5
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if wins %}
+[wins]
+{% for server in wins -%}
+wins{{ loop.index }}={{ server }}
+{% endfor -%}
+{% endif %}
+
+
+[pptp]
+ifname=pptp%d
+{% if outside_addr %}
+bind={{ outside_addr }}
+{% endif %}
+verbose=1
+ppp-max-mtu={{mtu}}
+mppe={{ ppp_mppe }}
+echo-interval=10
+echo-failure=3
+
+
+[client-ip-range]
+0.0.0.0/0
+
+[ip-pool]
+tunnel={{ client_ip_pool }}
+gw-ip-address={{ gw_ip }}
+
+[ppp]
+verbose=5
+check-ip=1
+single-session=replace
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
+{% endfor -%}
+
+acct-timeout={{ radius_acct_tmo }}
+timeout={{ radius_timeout }}
+max-try={{ radius_max_try }}
+
+{% if radius_nas_id %}
+nas-identifier={{ radius_nas_id }}
+{% endif -%}
+{% if radius_nas_ip %}
+nas-ip-address={{ radius_nas_ip }}
+{% endif -%}
+{% if radius_source_address %}
+bind={{ radius_source_address }}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2003
+
diff --git a/data/templates/accel-ppp/sstp.config.tmpl b/data/templates/accel-ppp/sstp.config.tmpl
new file mode 100644
index 000000000..c9e4a1d7d
--- /dev/null
+++ b/data/templates/accel-ppp/sstp.config.tmpl
@@ -0,0 +1,146 @@
+### generated by vpn_sstp.py ###
+[modules]
+log_syslog
+sstp
+shaper
+{% if auth_mode == 'local' %}
+chap-secrets
+{% elif auth_mode == 'radius' %}
+radius
+{% endif -%}
+ippool
+ipv6pool
+ipv6_nd
+ipv6_dhcp
+
+{% for proto in auth_proto %}
+{{proto}}
+{% endfor %}
+
+[core]
+thread-count={{thread_cnt}}
+
+[common]
+single-session=replace
+
+[log]
+syslog=accel-sstp,daemon
+copy=1
+level=5
+
+[client-ip-range]
+disable
+
+[sstp]
+verbose=1
+ifname=sstp%d
+accept=ssl
+ssl-ca-file={{ ssl_ca }}
+ssl-pemfile={{ ssl_cert }}
+ssl-keyfile={{ ssl_key }}
+
+{% if client_ip_pool %}
+[ip-pool]
+gw-ip-address={{ client_gateway }}
+{% for subnet in client_ip_pool %}
+{{ subnet }}
+{% endfor %}
+{% endif %}
+
+{% if dnsv4 %}
+[dns]
+{% for dns in dnsv4 -%}
+dns{{ loop.index }}={{ dns }}
+{% endfor -%}
+{% endif %}
+
+{% if dnsv6 %}
+[ipv6-dns]
+{% for dns in dnsv6 -%}
+{{ dns }}
+{% endfor -%}
+{% endif %}
+
+
+{% if auth_mode == 'local' %}
+[chap-secrets]
+chap-secrets={{ chap_secrets_file }}
+{% elif auth_mode == 'radius' %}
+[radius]
+verbose=1
+{% for r in radius_server %}
+server={{ r.server }},{{ r.key }},auth-port={{ r.port }},acct-port={{ r.acct_port }},req-limit=0,fail-time={{ r.fail_time }}
+{% endfor -%}
+
+acct-timeout={{ radius_acct_tmo }}
+timeout={{ radius_timeout }}
+max-try={{ radius_max_try }}
+
+{% if radius_nas_id %}
+nas-identifier={{ radius_nas_id }}
+{% endif -%}
+{% if radius_nas_ip %}
+nas-ip-address={{ radius_nas_ip }}
+{% endif -%}
+{% if radius_source_address %}
+bind={{ radius_source_address }}
+{% endif -%}
+
+
+{% if radius_dynamic_author %}
+dae-server={{ radius_dynamic_author.server }}:{{ radius_dynamic_author.port }},{{ radius_dynamic_author.key }}
+{% endif -%}
+{% endif %}
+{% if client_gateway %}
+gw-ip-address={{ client_gateway }}
+{% endif %}
+
+[ppp]
+verbose=1
+check-ip=1
+{% if mtu %}
+mtu={{ mtu }}
+{% endif -%}
+{% if client_ipv6_pool %}
+ipv6=allow
+{% endif %}
+
+{% if ppp_mppe %}
+mppe={{ ppp_mppe }}
+{% endif -%}
+{% if ppp_echo_interval %}
+lcp-echo-interval={{ ppp_echo_interval }}
+{% endif -%}
+{% if ppp_echo_failure %}
+lcp-echo-failure={{ ppp_echo_failure }}
+{% endif -%}
+{% if ppp_echo_timeout %}
+lcp-echo-timeout={{ ppp_echo_timeout }}
+{% endif %}
+
+{% if client_ipv6_pool %}
+[ipv6-pool]
+{% for p in client_ipv6_pool %}
+{{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% for p in client_ipv6_delegate_prefix %}
+delegate={{ p.prefix }},{{ p.mask }}
+{% endfor %}
+{% endif %}
+
+{% if client_ipv6_delegate_prefix %}
+[ipv6-dhcp]
+verbose=1
+{% endif %}
+
+{% if radius_shaper_attr %}
+[shaper]
+verbose=1
+attr={{ radius_shaper_attr }}
+{% if radius_shaper_vendor %}
+vendor={{ radius_shaper_vendor }}
+{% endif -%}
+{% endif %}
+
+[cli]
+tcp=127.0.0.1:2005
diff --git a/data/templates/bcast-relay/udp-broadcast-relay.tmpl b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
new file mode 100644
index 000000000..d0c7d8bf9
--- /dev/null
+++ b/data/templates/bcast-relay/udp-broadcast-relay.tmpl
@@ -0,0 +1,7 @@
+### Autogenerated by bcast_relay.py ###
+
+# UDP broadcast relay configuration for instance {{ id }}
+{%- if description %}
+# Comment: {{ description }}
+{% endif %}
+DAEMON_ARGS="{{ '-s ' + address if address is defined }} {{ instance }} {{ port }} {{ interface | join(' ') }}"
diff --git a/data/templates/conserver/conserver.conf.tmpl b/data/templates/conserver/conserver.conf.tmpl
new file mode 100644
index 000000000..4e7b5d8d7
--- /dev/null
+++ b/data/templates/conserver/conserver.conf.tmpl
@@ -0,0 +1,37 @@
+### Autogenerated by service_console-server.py ###
+
+# See https://www.conserver.com/docs/conserver.cf.man.html for additional options
+
+config * {
+ primaryport 3109;
+ daemonmode false;
+}
+
+default * {
+ motd "VyOS Console Server";
+ rw *;
+}
+
+##
+## list of consoles we serve
+##
+{% for key, value in device.items() %}
+{# Depending on our USB serial console we could require a path adjustment #}
+{% set path = '/dev' if key.startswith('ttyS') else '/dev/serial/by-bus' %}
+console {{ key }} {
+ master localhost;
+ type device;
+ device {{ path }}/{{ key }};
+ baud {{ value.speed }};
+ parity {{ value.parity }};
+ options {{ "!" if value.stop_bits == "1" }}cstopb;
+}
+{% endfor %}
+
+##
+## list of clients we allow
+##
+access * {
+ trusted localhost;
+ allowed localhost;
+}
diff --git a/data/templates/dhcp-client/daemon-options.tmpl b/data/templates/dhcp-client/daemon-options.tmpl
new file mode 100644
index 000000000..290aefa49
--- /dev/null
+++ b/data/templates/dhcp-client/daemon-options.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by interface.py ###
+
+DHCLIENT_OPTS="-nw -cf /var/lib/dhcp/dhclient_{{ifname}}.conf -pf /var/lib/dhcp/dhclient_{{ifname}}.pid -lf /var/lib/dhcp/dhclient_{{ifname}}.leases {{ifname}}"
+
diff --git a/data/templates/dhcp-client/ipv4.tmpl b/data/templates/dhcp-client/ipv4.tmpl
new file mode 100644
index 000000000..8a44a9761
--- /dev/null
+++ b/data/templates/dhcp-client/ipv4.tmpl
@@ -0,0 +1,19 @@
+### Autogenerated by interface.py ###
+
+option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+timeout 60;
+retry 300;
+
+interface "{{ ifname }}" {
+ send host-name "{{ dhcp_options.host_name }}";
+{% if dhcp_options.client_id is defined and dhcp_options.client_id is not none %}
+ send dhcp-client-identifier "{{ dhcp_options.client_id }}";
+{% endif %}
+{% if dhcp_options.vendor_class_id is defined and dhcp_options.vendor_class_id is not none %}
+ send vendor-class-identifier "{{ dhcp_options.vendor_class_id }}";
+{% endif %}
+ request subnet-mask, broadcast-address, routers, domain-name-servers,
+ rfc3442-classless-static-routes, domain-name, interface-mtu;
+ require subnet-mask;
+}
+
diff --git a/data/templates/dhcp-client/ipv6.tmpl b/data/templates/dhcp-client/ipv6.tmpl
new file mode 100644
index 000000000..68f668117
--- /dev/null
+++ b/data/templates/dhcp-client/ipv6.tmpl
@@ -0,0 +1,57 @@
+### Autogenerated by interface.py ###
+
+# man https://www.unix.com/man-page/debian/5/dhcp6c.conf/
+interface {{ ifname }} {
+{% if address is defined and 'dhcpv6' in address %}
+ request domain-name-servers;
+ request domain-name;
+{% if dhcpv6_options is defined and dhcpv6_options.parameters_only is defined %}
+ information-only;
+{% endif %}
+{% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %}
+ send ia-na 0; # non-temporary address
+{% endif %}
+{% if dhcpv6_options is defined and dhcpv6_options.rapid_commit is defined %}
+ send rapid-commit; # wait for immediate reply instead of advertisements
+{% endif %}
+{% endif %}
+{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
+{% for pd in dhcpv6_options.pd %}
+ send ia-pd {{ pd }}; # prefix delegation #{{ pd }}
+{% endfor %}
+{% endif %}
+};
+
+{% if address is defined and 'dhcpv6' in address %}
+{% if dhcpv6_options is not defined or dhcpv6_options.temporary is not defined %}
+id-assoc na 0 {
+ # Identity association for non temporary address
+};
+{% endif %}
+{% endif %}
+
+{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
+{% for pd in dhcpv6_options.pd %}
+id-assoc pd {{ pd }} {
+{# length got a default value #}
+ prefix ::/{{ dhcpv6_options.pd[pd].length }} infinity;
+{% set sla_len = 64 - dhcpv6_options.pd[pd].length|int %}
+{% set count = namespace(value=0) %}
+{% for interface in dhcpv6_options.pd[pd].interface if dhcpv6_options.pd[pd].interface is defined %}
+ prefix-interface {{ interface }} {
+ sla-len {{ sla_len }};
+{% if dhcpv6_options.pd[pd].interface[interface].sla_id is defined and dhcpv6_options.pd[pd].interface[interface].sla_id is not none %}
+ sla-id {{ dhcpv6_options.pd[pd].interface[interface].sla_id }};
+{% else %}
+ sla-id {{ count.value }};
+{% endif %}
+{% if dhcpv6_options.pd[pd].interface[interface].address is defined and dhcpv6_options.pd[pd].interface[interface].address is not none %}
+ ifid {{ dhcpv6_options.pd[pd].interface[interface].address }};
+{% endif %}
+ };
+{% set count.value = count.value + 1 %}
+{% endfor %}
+};
+{% endfor %}
+{% endif %}
+
diff --git a/data/templates/dhcp-relay/config.tmpl b/data/templates/dhcp-relay/config.tmpl
new file mode 100644
index 000000000..b223807cf
--- /dev/null
+++ b/data/templates/dhcp-relay/config.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by dhcp_relay.py ###
+
+# Defaults for isc-dhcp-relay6.service
+OPTIONS="{{ options | join(' ') }} -i {{ interface | join(' -i ') }} {{ server | join(' ') }}"
diff --git a/data/templates/dhcp-server/dhcpd.conf.tmpl b/data/templates/dhcp-server/dhcpd.conf.tmpl
new file mode 100644
index 000000000..5f5129451
--- /dev/null
+++ b/data/templates/dhcp-server/dhcpd.conf.tmpl
@@ -0,0 +1,195 @@
+
+### Autogenerated by dhcp_server.py ###
+
+# For options please consult the following website:
+# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
+#
+# log-facility local7;
+
+{% if hostfile_update %}
+on release {
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+}
+
+on expiry {
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".",leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":",substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "release", ClientName, ClientIp, ClientMac, ClientDomain);
+}
+{% endif %}
+{%- if host_decl_name %}
+use-host-decl-names on;
+{%- endif %}
+ddns-update-style {% if ddns_enable -%} interim {%- else -%} none {%- endif %};
+{% if static_route -%}
+option rfc3442-static-route code 121 = array of integer 8;
+option windows-static-route code 249 = array of integer 8;
+{%- endif %}
+{% if wpad -%}
+option wpad-url code 252 = text;
+{% endif %}
+
+{%- if global_parameters %}
+# The following {{ global_parameters | length }} line(s) were added as global-parameters in the CLI and have not been validated
+{%- for param in global_parameters %}
+{{ param }}
+{%- endfor -%}
+{%- endif %}
+
+# Failover configuration
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+{%- for subnet in network.subnet %}
+{%- if subnet.failover_name -%}
+failover peer "{{ subnet.failover_name }}" {
+{%- if subnet.failover_status == 'primary' %}
+ primary;
+ mclt 1800;
+ split 128;
+{%- elif subnet.failover_status == 'secondary' %}
+ secondary;
+{%- endif %}
+ address {{ subnet.failover_local_addr }};
+ port 520;
+ peer address {{ subnet.failover_peer_addr }};
+ peer port 520;
+ max-response-delay 30;
+ max-unacked-updates 10;
+ load balance max seconds 3;
+}
+{% endif -%}
+{% endfor -%}
+{% endif -%}
+{% endfor %}
+
+# Shared network configration(s)
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+shared-network {{ network.name }} {
+ {%- if network.authoritative %}
+ authoritative;
+ {%- endif %}
+ {%- if network.network_parameters %}
+ # The following {{ network.network_parameters | length }} line(s) were added as shared-network-parameters in the CLI and have not been validated
+ {%- for param in network.network_parameters %}
+ {{ param }}
+ {%- endfor %}
+ {%- endif %}
+ {%- for subnet in network.subnet %}
+ subnet {{ subnet.address }} netmask {{ subnet.netmask }} {
+ {%- if subnet.dns_server %}
+ option domain-name-servers {{ subnet.dns_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.domain_search %}
+ option domain-search {{ subnet.domain_search | join(', ') }};
+ {%- endif %}
+ {%- if subnet.ntp_server %}
+ option ntp-servers {{ subnet.ntp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.pop_server %}
+ option pop-server {{ subnet.pop_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.smtp_server %}
+ option smtp-server {{ subnet.smtp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.time_server %}
+ option time-servers {{ subnet.time_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.wins_server %}
+ option netbios-name-servers {{ subnet.wins_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.static_route %}
+ option rfc3442-static-route {{ subnet.static_route }}{% if subnet.rfc3442_default_router %}, {{ subnet.rfc3442_default_router }}{% endif %};
+ option windows-static-route {{ subnet.static_route }};
+ {%- endif %}
+ {%- if subnet.ip_forwarding %}
+ option ip-forwarding true;
+ {%- endif -%}
+ {%- if subnet.default_router %}
+ option routers {{ subnet.default_router }};
+ {%- endif -%}
+ {%- if subnet.server_identifier %}
+ option dhcp-server-identifier {{ subnet.server_identifier }};
+ {%- endif -%}
+ {%- if subnet.domain_name %}
+ option domain-name "{{ subnet.domain_name }}";
+ {%- endif -%}
+ {%- if subnet.subnet_parameters %}
+ # The following {{ subnet.subnet_parameters | length }} line(s) were added as subnet-parameters in the CLI and have not been validated
+ {%- for param in subnet.subnet_parameters %}
+ {{ param }}
+ {%- endfor -%}
+ {%- endif %}
+ {%- if subnet.tftp_server %}
+ option tftp-server-name "{{ subnet.tftp_server }}";
+ {%- endif -%}
+ {%- if subnet.bootfile_name %}
+ option bootfile-name "{{ subnet.bootfile_name }}";
+ filename "{{ subnet.bootfile_name }}";
+ {%- endif -%}
+ {%- if subnet.bootfile_server %}
+ next-server {{ subnet.bootfile_server }};
+ {%- endif -%}
+ {%- if subnet.time_offset %}
+ option time-offset {{ subnet.time_offset }};
+ {%- endif -%}
+ {%- if subnet.wpad_url %}
+ option wpad-url "{{ subnet.wpad_url }}";
+ {%- endif -%}
+ {%- if subnet.client_prefix_length %}
+ option subnet-mask {{ subnet.client_prefix_length }};
+ {%- endif -%}
+ {% if subnet.lease %}
+ default-lease-time {{ subnet.lease }};
+ max-lease-time {{ subnet.lease }};
+ {%- endif -%}
+ {%- for host in subnet.static_mapping %}
+ {% if not host.disabled -%}
+ host {% if host_decl_name -%} {{ host.name }} {%- else -%} {{ network.name }}_{{ host.name }} {%- endif %} {
+ {%- if host.ip_address %}
+ fixed-address {{ host.ip_address }};
+ {%- endif %}
+ hardware ethernet {{ host.mac_address }};
+ {%- if host.static_parameters %}
+ # The following {{ host.static_parameters | length }} line(s) were added as static-mapping-parameters in the CLI and have not been validated
+ {%- for param in host.static_parameters %}
+ {{ param }}
+ {%- endfor -%}
+ {%- endif %}
+ }
+ {%- endif %}
+ {%- endfor %}
+ {%- if subnet.failover_name %}
+ pool {
+ failover peer "{{ subnet.failover_name }}";
+ deny dynamic bootp clients;
+ {%- for range in subnet.range %}
+ range {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ }
+ {%- else %}
+ {%- for range in subnet.range %}
+ range {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ {%- endif %}
+ }
+ {%- endfor %}
+ on commit {
+ set shared-networkname = "{{ network.name }}";
+ {% if hostfile_update -%}
+ set ClientName = pick-first-value(host-decl-name, option fqdn.hostname, option host-name);
+ set ClientIp = binary-to-ascii(10, 8, ".", leased-address);
+ set ClientMac = binary-to-ascii(16, 8, ":", substring(hardware, 1, 6));
+ set ClientDomain = pick-first-value(config-option domain-name, "..YYZ!");
+ execute("/usr/libexec/vyos/system/on-dhcp-event.sh", "commit", ClientName, ClientIp, ClientMac, ClientDomain);
+ {%- endif %}
+ }
+}
+{%- endif %}
+{% endfor %}
diff --git a/data/templates/dhcpv6-relay/config.tmpl b/data/templates/dhcpv6-relay/config.tmpl
new file mode 100644
index 000000000..55035ae6c
--- /dev/null
+++ b/data/templates/dhcpv6-relay/config.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by dhcpv6_relay.py ###
+
+# Defaults for isc-dhcp-relay6.service
+OPTIONS="-l {{ listen_addr | join(' -l ') }} -u {{ upstream_addr | join(' -u ') }} {{ options | join(' ') }}"
diff --git a/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
new file mode 100644
index 000000000..ff7822b0d
--- /dev/null
+++ b/data/templates/dhcpv6-server/dhcpdv6.conf.tmpl
@@ -0,0 +1,81 @@
+### Autogenerated by dhcpv6_server.py ###
+
+# For options please consult the following website:
+# https://www.isc.org/wp-content/uploads/2017/08/dhcp43options.html
+
+log-facility local7;
+{%- if preference %}
+option dhcp6.preference {{ preference }};
+{%- endif %}
+
+# Shared network configration(s)
+{% for network in shared_network %}
+{%- if not network.disabled -%}
+shared-network {{ network.name }} {
+ {%- for subnet in network.subnet %}
+ subnet6 {{ subnet.network }} {
+ {%- for range in subnet.range6_prefix %}
+ range6 {{ range.prefix }}{{ " temporary" if range.temporary }};
+ {%- endfor %}
+ {%- for range in subnet.range6 %}
+ range6 {{ range.start }} {{ range.stop }};
+ {%- endfor %}
+ {%- if subnet.domain_search %}
+ option dhcp6.domain-search "{{ subnet.domain_search | join('", "') }}";
+ {%- endif %}
+ {%- if subnet.lease_def %}
+ default-lease-time {{ subnet.lease_def }};
+ {%- endif %}
+ {%- if subnet.lease_max %}
+ max-lease-time {{ subnet.lease_max }};
+ {%- endif %}
+ {%- if subnet.lease_min %}
+ min-lease-time {{ subnet.lease_min }};
+ {%- endif %}
+ {%- if subnet.dns_server %}
+ option dhcp6.name-servers {{ subnet.dns_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.nis_domain %}
+ option dhcp6.nis-domain-name "{{ subnet.nis_domain }}";
+ {%- endif %}
+ {%- if subnet.nis_server %}
+ option dhcp6.nis-servers {{ subnet.nis_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.nisp_domain %}
+ option dhcp6.nisp-domain-name "{{ subnet.nisp_domain }}";
+ {%- endif %}
+ {%- if subnet.nisp_server %}
+ option dhcp6.nisp-servers {{ subnet.nisp_server | join(', ') }};
+ {%- endif %}
+ {%- if subnet.sip_address %}
+ option dhcp6.sip-servers-addresses {{ subnet.sip_address | join(', ') }};
+ {%- endif %}
+ {%- if subnet.sip_hostname %}
+ option dhcp6.sip-servers-names "{{ subnet.sip_hostname | join('", "') }}";
+ {%- endif %}
+ {%- if subnet.sntp_server %}
+ option dhcp6.sntp-servers {{ subnet.sntp_server | join(', ') }};
+ {%- endif %}
+ {%- for prefix in subnet.prefix_delegation %}
+ prefix6 {{ prefix.start }} {{ prefix.stop }} /{{ prefix.length }};
+ {%- endfor %}
+ {%- for host in subnet.static_mapping %}
+ {% if not host.disabled -%}
+ host {{ network.name }}_{{ host.name }} {
+ {%- if host.client_identifier %}
+ host-identifier option dhcp6.client-id {{ host.client_identifier }};
+ {%- endif %}
+ {%- if host.ipv6_address %}
+ fixed-address6 {{ host.ipv6_address }};
+ {%- endif %}
+ }
+ {%- endif %}
+ {%- endfor %}
+ }
+ {%- endfor %}
+ on commit {
+ set shared-networkname = "{{ network.name }}";
+ }
+}
+{%- endif %}
+{% endfor %}
diff --git a/data/templates/dns-forwarding/recursor.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.conf.lua.tmpl
new file mode 100644
index 000000000..e2506238d
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.conf.lua.tmpl
@@ -0,0 +1,9 @@
+-- Autogenerated by VyOS (dns_forwarding.py) --
+-- Do not edit, your changes will get overwritten --
+
+-- Load DNSSEC root keys from dns-root-data package.
+dofile("/usr/share/pdns-recursor/lua-config/rootkeys.lua")
+
+-- Load lua from vyos-hostsd --
+dofile("recursor.vyos-hostsd.conf.lua")
+
diff --git a/data/templates/dns-forwarding/recursor.conf.tmpl b/data/templates/dns-forwarding/recursor.conf.tmpl
new file mode 100644
index 000000000..d233b8abc
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.conf.tmpl
@@ -0,0 +1,33 @@
+### Autogenerated by dns_forwarding.py ###
+
+# XXX: pdns recursor doesn't like whitespace near entry separators,
+# especially in the semicolon-separated lists of name servers.
+# Please be careful if you edit the template.
+
+# Non-configurable defaults
+daemon=yes
+threads=1
+allow-from={{ allow_from | join(',') }}
+log-common-errors=yes
+non-local-bind=yes
+query-local-address=0.0.0.0
+query-local-address6=::
+lua-config-file=recursor.conf.lua
+
+# cache-size
+max-cache-entries={{ cache_size }}
+
+# negative TTL for NXDOMAIN
+max-negative-ttl={{ negative_ttl }}
+
+# ignore-hosts-file
+export-etc-hosts={{ export_hosts_file }}
+
+# listen-address
+local-address={{ listen_address | join(',') }}
+
+# dnssec
+dnssec={{ dnssec }}
+
+forward-zones-file=recursor.forward-zones.conf
+
diff --git a/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl
new file mode 100644
index 000000000..de5eaee00
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.forward-zones.conf.tmpl
@@ -0,0 +1,28 @@
+# Autogenerated by VyOS (vyos-hostsd)
+# Do not edit, your changes will get overwritten
+
+# dot zone (catch-all): '+' indicates recursion is desired
+# (same as forward-zones-recurse)
+{#- the code below ensures the order of nameservers is determined first by #}
+{#- the order of tags, then by the order of nameservers within that tag #}
+{%- set n = namespace(dot_zone_ns='') %}
+{%- for tag in name_server_tags_recursor %}
+{%- set ns = '' %}
+{%- if tag in name_servers %}
+{%- set ns = ns + name_servers[tag]|join(', ') %}
+{%- set n.dot_zone_ns = (n.dot_zone_ns, ns)|join(', ') if n.dot_zone_ns != '' else ns %}
+{%- endif %}
+# {{ tag }}: {{ ns }}
+{%- endfor %}
+
+{%- if n.dot_zone_ns %}
++.={{ n.dot_zone_ns }}
+{%- endif %}
+
+{% if forward_zones -%}
+# zones added via 'service dns forwarding domain'
+{%- for zone, zonedata in forward_zones.items() %}
+{% if zonedata['recursion-desired'] %}+{% endif %}{{ zone }}={{ zonedata['nslist']|join(', ') }}
+{%- endfor %}
+{%- endif %}
+
diff --git a/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl
new file mode 100644
index 000000000..b0d99d9ae
--- /dev/null
+++ b/data/templates/dns-forwarding/recursor.vyos-hostsd.conf.lua.tmpl
@@ -0,0 +1,24 @@
+-- Autogenerated by VyOS (vyos-hostsd) --
+-- Do not edit, your changes will get overwritten --
+
+{% if hosts -%}
+-- from 'system static-host-mapping' and DHCP server
+{%- for tag, taghosts in hosts.items() %}
+{%- for host, hostprops in taghosts.items() %}
+addNTA("{{ host }}.", "{{ tag }}")
+{%- for a in hostprops['aliases'] %}
+addNTA("{{ a }}.", "{{ tag }} alias")
+{%- endfor %}
+{%- endfor %}
+{%- endfor %}
+{%- endif %}
+
+{% if forward_zones -%}
+-- from 'service dns forwarding domain'
+{%- for zone, zonedata in forward_zones.items() %}
+{%- if zonedata['addNTA'] %}
+addNTA("{{ zone }}", "static")
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
diff --git a/data/templates/dynamic-dns/ddclient.conf.tmpl b/data/templates/dynamic-dns/ddclient.conf.tmpl
new file mode 100644
index 000000000..9c7219230
--- /dev/null
+++ b/data/templates/dynamic-dns/ddclient.conf.tmpl
@@ -0,0 +1,46 @@
+### Autogenerated by dynamic_dns.py ###
+daemon=1m
+syslog=yes
+ssl=yes
+
+{% for interface in interfaces -%}
+
+#
+# ddclient configuration for interface "{{ interface.interface }}":
+#
+{% if interface.web_url -%}
+use=web, web='{{ interface.web_url}}' {%- if interface.web_skip %}, web-skip='{{ interface.web_skip }}'{% endif %}
+{% else -%}
+use=if, if={{ interface.interface }}
+{% endif -%}
+
+{% for rfc in interface.rfc2136 -%}
+{% for record in rfc.record %}
+# RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }}
+server={{ rfc.server }}
+protocol=nsupdate
+password={{ rfc.keyfile }}
+ttl={{ rfc.ttl }}
+zone={{ rfc.zone }}
+{{ record }}
+{% endfor -%}
+{% endfor -%}
+
+{% for srv in interface.service %}
+{% for host in srv.host %}
+# DynDNS provider configuration for {{ host }}
+protocol={{ srv.protocol }},
+max-interval=28d,
+login={{ srv.login }},
+password='{{ srv.password }}',
+{% if srv.server -%}
+server={{ srv.server }},
+{% endif -%}
+{% if srv.zone -%}
+zone={{ srv.zone }},
+{% endif -%}
+{{ host }}
+{% endfor %}
+{% endfor %}
+
+{% endfor %}
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
new file mode 100644
index 000000000..0c29f536b
--- /dev/null
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -0,0 +1,157 @@
+#!/usr/sbin/nft -f
+
+# Start with clean NAT table
+flush table nat
+
+{% if helper_functions == 'remove' %}
+{# NAT if going to be disabled - remove rules and targets from nftables #}
+
+{% set base_command = "delete rule ip raw" %}
+{{ base_command }} PREROUTING handle {{ pre_ct_ignore }}
+{{ base_command }} OUTPUT handle {{ out_ct_ignore }}
+{{ base_command }} PREROUTING handle {{ pre_ct_conntrack }}
+{{ base_command }} OUTPUT handle {{ out_ct_conntrack }}
+
+delete chain ip raw NAT_CONNTRACK
+
+{% elif helper_functions == 'add' %}
+{# NAT if enabled - add targets to nftables #}
+add chain ip raw NAT_CONNTRACK
+add rule ip raw NAT_CONNTRACK counter accept
+
+{% set base_command = "add rule ip raw" %}
+
+{{ base_command }} PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
+{{ base_command }} OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
+{{ base_command }} PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+{{ base_command }} OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% endif %}
+
+{% macro nat_rule(rule, chain) %}
+{% set src_addr = "ip saddr " + rule.source_address if rule.source_address %}
+{% set dst_addr = "ip daddr " + rule.dest_address if rule.dest_address %}
+
+{# negated port groups need special treatment, move != in front of { } group #}
+{% if rule.source_port.startswith('!=') %}
+{% set src_port = "sport != { " + rule.source_port.replace('!=','') +" }" if rule.source_port %}
+{% else %}
+{% set src_port = "sport { " + rule.source_port +" }" if rule.source_port %}
+{% endif %}
+
+{# negated port groups need special treatment, move != in front of { } group #}
+{% if rule.dest_port.startswith('!=') %}
+{% set dst_port = "dport != { " + rule.dest_port.replace('!=','') +" }" if rule.dest_port %}
+{% else %}
+{% set dst_port = "dport { " + rule.dest_port +" }" if rule.dest_port %}
+{% endif %}
+
+{% set comment = "DST-NAT-" + rule.number %}
+
+{% if chain == "PREROUTING" %}
+{% set interface = " iifname \"" + rule.interface_in + "\"" if rule.interface_in is defined and rule.interface_in != 'any' else '' %}
+{% set trns_addr = "dnat to " + rule.translation_address %}
+
+{% elif chain == "POSTROUTING" %}
+{% set interface = " oifname \"" + rule.interface_out + "\"" if rule.interface_out is defined and rule.interface_out != 'any' else '' %}
+{% if rule.translation_address == 'masquerade' %}
+{% set trns_addr = rule.translation_address %}
+{% if rule.translation_port %}
+{% set trns_addr = trns_addr + " to " %}
+{% endif %}
+{% else %}
+{% set trns_addr = "snat to " + rule.translation_address %}
+{% endif %}
+{% endif %}
+{% set trns_port = ":" + rule.translation_port if rule.translation_port %}
+
+{% if rule.protocol == "tcp_udp" %}
+{% set protocol = "tcp" %}
+{% set comment = comment + " tcp_udp" %}
+{% else %}
+{% set protocol = rule.protocol %}
+{% endif %}
+
+{% if rule.log %}
+{% set base_log = "[NAT-DST-" + rule.number %}
+{% if rule.exclude %}
+{% set log = base_log + "-EXCL]" %}
+{% elif rule.translation_address == 'masquerade' %}
+{% set log = base_log + "-MASQ]" %}
+{% else %}
+{% set log = base_log + "]" %}
+{% endif %}
+{% endif %}
+
+{% if rule.exclude %}
+{# rule has been marked as "exclude" thus we simply return here #}
+{% set trns_addr = "return" %}
+{% set trns_port = "" %}
+{% endif %}
+
+{% set output = "add rule ip nat " + chain + interface %}
+
+{% if protocol != "all" %}
+{% set output = output + " ip protocol " + protocol %}
+{% endif %}
+
+{% if src_addr %}
+{% set output = output + " " + src_addr %}
+{% endif %}
+{% if src_port %}
+{% set output = output + " " + protocol + " " + src_port %}
+{% endif %}
+
+{% if dst_addr %}
+{% set output = output + " " + dst_addr %}
+{% endif %}
+{% if dst_port %}
+{% set output = output + " " + protocol + " " + dst_port %}
+{% endif %}
+
+{# Count packets #}
+{% set output = output + " counter" %}
+
+{# Special handling of log option, we must repeat the entire rule before the #}
+{# NAT translation options are added, this is essential #}
+{% if log %}
+{% set log_output = output + " log prefix \"" + log + "\" comment \"" + comment + "\"" %}
+{% endif %}
+
+{% if trns_addr %}
+{% set output = output + " " + trns_addr %}
+{% endif %}
+
+{% if trns_port %}
+{# Do not add a whitespace here, translation port must be directly added after IP address #}
+{# e.g. 192.0.2.10:3389 #}
+{% set output = output + trns_port %}
+{% endif %}
+
+{% if comment %}
+{% set output = output + " comment \"" + comment + "\"" %}
+{% endif %}
+
+{{ log_output if log_output }}
+{{ output }}
+
+{# Special handling if protocol is tcp_udp, we must repeat the entire rule with udp as protocol #}
+{% if rule.protocol == "tcp_udp" %}
+{# Beware of trailing whitespace, without it the comment tcp_udp will be changed to udp_udp #}
+{{ log_output | replace("tcp ", "udp ") if log_output }}
+{{ output | replace("tcp ", "udp ") }}
+{% endif %}
+{% endmacro %}
+
+#
+# Destination NAT rules build up here
+#
+{% for rule in destination if not rule.disabled -%}
+{{ nat_rule(rule, 'PREROUTING') }}
+{% endfor %}
+
+#
+# Source NAT rules build up here
+#
+{% for rule in source if not rule.disabled -%}
+{{ nat_rule(rule, 'POSTROUTING') }}
+{% endfor %}
diff --git a/data/templates/frr/bfd.frr.tmpl b/data/templates/frr/bfd.frr.tmpl
new file mode 100644
index 000000000..7df4bfd01
--- /dev/null
+++ b/data/templates/frr/bfd.frr.tmpl
@@ -0,0 +1,16 @@
+!
+bfd
+{% for peer in old_peers -%}
+ no peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
+{% endfor -%}
+!
+{% for peer in new_peers -%}
+ peer {{ peer.remote }}{% if peer.multihop %} multihop{% endif %}{% if peer.src_addr %} local-address {{ peer.src_addr }}{% endif %}{% if peer.src_if %} interface {{ peer.src_if }}{% endif %}
+ detect-multiplier {{ peer.multiplier }}
+ receive-interval {{ peer.rx_interval }}
+ transmit-interval {{ peer.tx_interval }}
+ {% if peer.echo_mode %}echo-mode{% endif %}
+ {% if peer.echo_interval != '' %}echo-interval {{ peer.echo_interval }}{% endif %}
+ {% if not peer.shutdown %}no {% endif %}shutdown
+{% endfor -%}
+!
diff --git a/data/templates/frr/bgp.frr.tmpl b/data/templates/frr/bgp.frr.tmpl
new file mode 100644
index 000000000..cdf4cb4fe
--- /dev/null
+++ b/data/templates/frr/bgp.frr.tmpl
@@ -0,0 +1 @@
+!
diff --git a/data/templates/frr/igmp.frr.tmpl b/data/templates/frr/igmp.frr.tmpl
new file mode 100644
index 000000000..de4696c1f
--- /dev/null
+++ b/data/templates/frr/igmp.frr.tmpl
@@ -0,0 +1,41 @@
+!
+{% for iface in old_ifaces -%}
+interface {{ iface }}
+{% for group in old_ifaces[iface].gr_join -%}
+{% if old_ifaces[iface].gr_join[group] -%}
+{% for source in old_ifaces[iface].gr_join[group] -%}
+no ip igmp join {{ group }} {{ source }}
+{% endfor -%}
+{% else -%}
+no ip igmp join {{ group }}
+{% endif -%}
+{% endfor -%}
+no ip igmp
+!
+{% endfor -%}
+{% for iface in ifaces -%}
+interface {{ iface }}
+{% if ifaces[iface].version -%}
+ip igmp version {{ ifaces[iface].version }}
+{% else -%}
+{# IGMP default version 3 #}
+ip igmp
+{% endif -%}
+{% if ifaces[iface].query_interval -%}
+ip igmp query-interval {{ ifaces[iface].query_interval }}
+{% endif -%}
+{% if ifaces[iface].query_max_resp_time -%}
+ip igmp query-max-response-time {{ ifaces[iface].query_max_resp_time }}
+{% endif -%}
+{% for group in ifaces[iface].gr_join -%}
+{% if ifaces[iface].gr_join[group] -%}
+{% for source in ifaces[iface].gr_join[group] -%}
+ip igmp join {{ group }} {{ source }}
+{% endfor -%}
+{% else -%}
+ip igmp join {{ group }}
+{% endif -%}
+{% endfor -%}
+!
+{% endfor -%}
+!
diff --git a/data/templates/frr/ldpd.frr.tmpl b/data/templates/frr/ldpd.frr.tmpl
new file mode 100644
index 000000000..dbaa917e8
--- /dev/null
+++ b/data/templates/frr/ldpd.frr.tmpl
@@ -0,0 +1,70 @@
+!
+{% if mpls_ldp -%}
+mpls ldp
+{% if old_router_id -%}
+no router-id {{ old_router_id }}
+{% endif -%}
+{% if router_id -%}
+router-id {{ router_id }}
+{% endif -%}
+{% for neighbor_id in old_ldp.neighbors -%}
+no neighbor {{neighbor_id}} password {{old_ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+{% for neighbor_id in ldp.neighbors -%}
+neighbor {{neighbor_id}} password {{ldp.neighbors[neighbor_id].password}}
+{% endfor -%}
+address-family ipv4
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv4 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv4 }}
+{% endif -%}
+{% if ldp.d_transp_ipv4 -%}
+discovery transport-address {{ ldp.d_transp_ipv4 }}
+{% endif -%}
+{% if old_ldp.hello_holdtime -%}
+no discovery hello holdtime {{ old_ldp.hello_holdtime }}
+{% endif -%}
+{% if ldp.hello_holdtime -%}
+discovery hello holdtime {{ ldp.hello_holdtime }}
+{% endif -%}
+{% if old_ldp.hello_interval -%}
+no discovery hello interval {{ old_ldp.hello_interval }}
+{% endif -%}
+{% if ldp.hello_interval -%}
+discovery hello interval {{ ldp.hello_interval }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+!
+exit-address-family
+!
+{% if ldp.d_transp_ipv6 -%}
+address-family ipv6
+label local allocate host-routes
+{% if old_ldp.d_transp_ipv6 -%}
+no discovery transport-address {{ old_ldp.d_transp_ipv6 }}
+{% endif -%}
+{% if ldp.d_transp_ipv6 -%}
+discovery transport-address {{ ldp.d_transp_ipv6 }}
+{% endif -%}
+{% for interface in old_ldp.interfaces -%}
+no interface {{interface}}
+{% endfor -%}
+{% for interface in ldp.interfaces -%}
+interface {{interface}}
+{% endfor -%}
+!
+exit-address-family
+{% else -%}
+no address-family ipv6
+{% endif -%}
+!
+{% else -%}
+no mpls ldp
+{% endif -%}
+!
diff --git a/data/templates/frr/pimd.frr.tmpl b/data/templates/frr/pimd.frr.tmpl
new file mode 100644
index 000000000..1d1532c60
--- /dev/null
+++ b/data/templates/frr/pimd.frr.tmpl
@@ -0,0 +1,34 @@
+!
+{% for rp_addr in old_pim.rp -%}
+{% for group in old_pim.rp[rp_addr] -%}
+no ip pim rp {{ rp_addr }} {{ group }}
+{% endfor -%}
+{% endfor -%}
+{% if old_pim.rp_keep_alive -%}
+no ip pim rp keep-alive-timer {{ old_pim.rp_keep_alive }}
+{% endif -%}
+{% for iface in old_pim.ifaces -%}
+interface {{ iface }}
+no ip pim
+!
+{% endfor -%}
+{% for iface in pim.ifaces -%}
+interface {{ iface }}
+ip pim
+{% if pim.ifaces[iface].dr_prio -%}
+ip pim drpriority {{ pim.ifaces[iface].dr_prio }}
+{% endif -%}
+{% if pim.ifaces[iface].hello -%}
+ip pim hello {{ pim.ifaces[iface].hello }}
+{% endif -%}
+!
+{% endfor -%}
+{% for rp_addr in pim.rp -%}
+{% for group in pim.rp[rp_addr] -%}
+ip pim rp {{ rp_addr }} {{ group }}
+{% endfor -%}
+{% endfor -%}
+{% if pim.rp_keep_alive -%}
+ip pim rp keep-alive-timer {{ pim.rp_keep_alive }}
+{% endif -%}
+!
diff --git a/data/templates/frr/rip.frr.tmpl b/data/templates/frr/rip.frr.tmpl
new file mode 100644
index 000000000..60bc686bd
--- /dev/null
+++ b/data/templates/frr/rip.frr.tmpl
@@ -0,0 +1,143 @@
+!
+{% if rip_conf -%}
+router rip
+{% if old_default_distance -%}
+no distance {{old_default_distance}}
+{% endif -%}
+{% if default_distance -%}
+distance {{default_distance}}
+{% endif -%}
+{% if old_default_originate -%}
+no default-information originate
+{% endif -%}
+{% if default_originate -%}
+default-information originate
+{% endif -%}
+{% if old_rip.default_metric -%}
+no default-metric {{old_rip.default_metric}}
+{% endif -%}
+{% if rip.default_metric -%}
+default-metric {{rip.default_metric}}
+{% endif -%}
+{% for protocol in old_rip.redist -%}
+{% if old_rip.redist[protocol]['metric'] and old_rip.redist[protocol]['route_map'] -%}
+no redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
+{% elif old_rip.redist[protocol]['metric'] -%}
+no redistribute {{protocol}} metric {{old_rip.redist[protocol]['metric']}}
+{% elif old_rip.redist[protocol]['route_map'] -%}
+no redistribute {{protocol}} route-map {{old_rip.redist[protocol]['route_map']}}
+{% else -%}
+no redistribute {{protocol}}
+{% endif -%}
+{% endfor -%}
+{% for protocol in rip.redist -%}
+{% if rip.redist[protocol]['metric'] and rip.redist[protocol]['route_map'] -%}
+redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}} route-map {{rip.redist[protocol]['route_map']}}
+{% elif rip.redist[protocol]['metric'] -%}
+redistribute {{protocol}} metric {{rip.redist[protocol]['metric']}}
+{% elif rip.redist[protocol]['route_map'] -%}
+redistribute {{protocol}} route-map {{rip.redist[protocol]['route_map']}}
+{% else -%}
+redistribute {{protocol}}
+{% endif -%}
+{% endfor -%}
+{% for iface in old_rip.distribute -%}
+{% if old_rip.distribute[iface].iface_access_list_in -%}
+no distribute-list {{old_rip.distribute[iface].iface_access_list_in}} in {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_access_list_out -%}
+no distribute-list {{old_rip.distribute[iface].iface_access_list_out}} out {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_prefix_list_in -%}
+no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
+{% endif -%}
+{% if old_rip.distribute[iface].iface_prefix_list_out -%}
+no distribute-list prefix {{old_rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
+{% endif -%}
+{% endfor -%}
+{% for iface in rip.distribute -%}
+{% if rip.distribute[iface].iface_access_list_in -%}
+distribute-list {{rip.distribute[iface].iface_access_list_in}} in {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_access_list_out -%}
+distribute-list {{rip.distribute[iface].iface_access_list_out}} out {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_prefix_list_in -%}
+distribute-list prefix {{rip.distribute[iface].iface_prefix_list_in}} in {{iface}}
+{% endif -%}
+{% if rip.distribute[iface].iface_prefix_list_out -%}
+distribute-list prefix {{rip.distribute[iface].iface_prefix_list_out}} out {{iface}}
+{% endif -%}
+{% endfor -%}
+{% if old_rip.dist_acl_in -%}
+no distribute-list {{old_rip.dist_acl_in}} in
+{% endif -%}
+{% if rip.dist_acl_in -%}
+distribute-list {{rip.dist_acl_in}} in
+{% endif -%}
+{% if old_rip.dist_acl_out -%}
+no distribute-list {{old_rip.dist_acl_out}} out
+{% endif -%}
+{% if rip.dist_acl_out -%}
+distribute-list {{rip.dist_acl_out}} out
+{% endif -%}
+{% if old_rip.dist_prfx_in -%}
+no distribute-list prefix {{old_rip.dist_prfx_in}} in
+{% endif -%}
+{% if rip.dist_prfx_in -%}
+distribute-list prefix {{rip.dist_prfx_in}} in
+{% endif -%}
+{% if old_rip.dist_prfx_out -%}
+no distribute-list prefix {{old_rip.dist_prfx_out}} out
+{% endif -%}
+{% if rip.dist_prfx_out -%}
+distribute-list prefix {{rip.dist_prfx_out}} out
+{% endif -%}
+{% for network in old_rip.networks -%}
+no network {{network}}
+{% endfor -%}
+{% for network in rip.networks -%}
+network {{network}}
+{% endfor -%}
+{% for iface in old_rip.ifaces -%}
+no network {{iface}}
+{% endfor -%}
+{% for iface in rip.ifaces -%}
+network {{iface}}
+{% endfor -%}
+{% for neighbor in old_rip.neighbors -%}
+no neighbor {{neighbor}}
+{% endfor -%}
+{% for neighbor in rip.neighbors -%}
+neighbor {{neighbor}}
+{% endfor -%}
+{% for net in rip.net_distance -%}
+{% if rip.net_distance[net].access_list and rip.net_distance[net].distance -%}
+distance {{rip.net_distance[net].distance}} {{net}} {{rip.net_distance[net].access_list}}
+{% else -%}
+distance {{rip.net_distance[net].distance}} {{net}}
+{% endif -%}
+{% endfor -%}
+{% for passive_iface in old_rip.passive_iface -%}
+no passive-interface {{passive_iface}}
+{% endfor -%}
+{% for passive_iface in rip.passive_iface -%}
+passive-interface {{passive_iface}}
+{% endfor -%}
+{% for route in old_rip.route -%}
+no route {{route}}
+{% endfor -%}
+{% for route in rip.route -%}
+route {{route}}
+{% endfor -%}
+{% if old_rip.timer_update or old_rip.timer_timeout or old_rip.timer_garbage -%}
+no timers basic
+{% endif -%}
+{% if rip.timer_update or rip.timer_timeout or rip.timer_garbage -%}
+timers basic {{rip.timer_update}} {{rip.timer_timeout}} {{rip.timer_garbage}}
+{% endif -%}
+!
+{% else -%}
+no router rip
+!
+{% endif -%}
diff --git a/data/templates/frr/static_mcast.frr.tmpl b/data/templates/frr/static_mcast.frr.tmpl
new file mode 100644
index 000000000..86d619ab0
--- /dev/null
+++ b/data/templates/frr/static_mcast.frr.tmpl
@@ -0,0 +1,20 @@
+!
+{% for route_gr in old_mroute -%}
+{% for nh in old_mroute[route_gr] -%}
+{% if old_mroute[route_gr][nh] -%}
+no ip mroute {{ route_gr }} {{ nh }} {{ old_mroute[route_gr][nh] }}
+{% else -%}
+no ip mroute {{ route_gr }} {{ nh }}
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
+{% for route_gr in mroute -%}
+{% for nh in mroute[route_gr] -%}
+{% if mroute[route_gr][nh] -%}
+ip mroute {{ route_gr }} {{ nh }} {{ mroute[route_gr][nh] }}
+{% else -%}
+ip mroute {{ route_gr }} {{ nh }}
+{% endif -%}
+{% endfor -%}
+{% endfor -%}
+!
diff --git a/data/templates/getty/serial-getty.service.tmpl b/data/templates/getty/serial-getty.service.tmpl
new file mode 100644
index 000000000..0183eae7d
--- /dev/null
+++ b/data/templates/getty/serial-getty.service.tmpl
@@ -0,0 +1,37 @@
+[Unit]
+Description=Serial Getty on %I
+Documentation=man:agetty(8) man:systemd-getty-generator(8)
+Documentation=http://0pointer.de/blog/projects/serial-console.html
+BindsTo=dev-%i.device
+After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
+After=vyos-router.service
+
+# If additional gettys are spawned during boot then we should make
+# sure that this is synchronized before getty.target, even though
+# getty.target didn't actually pull it in.
+Before=getty.target
+IgnoreOnIsolate=yes
+
+# IgnoreOnIsolate causes issues with sulogin, if someone isolates
+# rescue.target or starts rescue.service from multi-user.target or
+# graphical.target.
+Conflicts=rescue.service
+Before=rescue.service
+
+[Service]
+# The '-o' option value tells agetty to replace 'login' arguments with an
+# option to preserve environment (-p), followed by '--' for safety, and then
+# the entered username.
+ExecStart=-/sbin/agetty -o '-p -- \\u' --keep-baud {{ speed }} %I $TERM
+Type=idle
+Restart=always
+UtmpIdentifier=%I
+TTYPath=/dev/%I
+TTYReset=yes
+TTYVHangup=yes
+KillMode=process
+IgnoreSIGPIPE=no
+SendSIGHUP=yes
+
+[Install]
+WantedBy=getty.target
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
new file mode 100644
index 000000000..a20be45ae
--- /dev/null
+++ b/data/templates/https/nginx.default.tmpl
@@ -0,0 +1,69 @@
+### Autogenerated by https.py ###
+# Default server configuration
+#
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server;
+ server_name _;
+ return 301 https://$server_name$request_uri;
+}
+
+{% for server in server_block_list %}
+server {
+
+ # SSL configuration
+ #
+{% if server.address == '*' %}
+ listen {{ server.port }} ssl;
+ listen [::]:{{ server.port }} ssl;
+{% else %}
+ listen {{ server.address }}:{{ server.port }} ssl;
+{% endif %}
+
+{% for name in server.name %}
+ server_name {{ name }};
+{% endfor %}
+
+{% if server.certbot %}
+ ssl_certificate {{ server.certbot_dir }}/live/{{ server.certbot_domain_dir }}/fullchain.pem;
+ ssl_certificate_key {{ server.certbot_dir }}/live/{{ server.certbot_domain_dir }}/privkey.pem;
+ include {{ server.certbot_dir }}/options-ssl-nginx.conf;
+ ssl_dhparam {{ server.certbot_dir }}/ssl-dhparams.pem;
+{% elif server.vyos_cert %}
+ include {{ server.vyos_cert.conf }};
+{% else %}
+ #
+ # Self signed certs generated by the ssl-cert package
+ # Don't use them in a production server!
+ #
+ include snippets/snakeoil.conf;
+{% endif %}
+
+ # proxy settings for HTTP API, if enabled; 503, if not
+ location ~ /(retrieve|configure|config-file|image|generate|show) {
+{% if server.api %}
+ proxy_pass http://localhost:{{ server.api.port }};
+ proxy_read_timeout 600;
+ proxy_buffering off;
+{% else %}
+ return 503;
+{% endif %}
+ }
+
+ error_page 501 502 503 =200 @50*_json;
+
+{% if api_set %}
+ location @50*_json {
+ default_type application/json;
+ return 200 '{"error": "service https api unavailable at this proxy address: set service https api-restrict virtual-host"}';
+ }
+{% else %}
+ location @50*_json {
+ default_type application/json;
+ return 200 '{"error": "Start service in configuration mode: set service https api"}';
+ }
+{% endif %}
+
+}
+
+{% endfor %}
diff --git a/data/templates/ids/fastnetmon.tmpl b/data/templates/ids/fastnetmon.tmpl
new file mode 100644
index 000000000..71a1b2bd7
--- /dev/null
+++ b/data/templates/ids/fastnetmon.tmpl
@@ -0,0 +1,60 @@
+# enable this option if you want to send logs to local syslog facility
+logging:local_syslog_logging = on
+
+# list of all your networks in CIDR format
+networks_list_path = /etc/networks_list
+
+# list networks in CIDR format which will be not monitored for attacks
+white_list_path = /etc/networks_whitelist
+
+# Enable/Disable any actions in case of attack
+enable_ban = on
+
+## How many packets will be collected from attack traffic
+ban_details_records_count = 500
+
+## How long (in seconds) we should keep an IP in blocked state
+## If you set 0 here it completely disables unban capability
+ban_time = 1900
+
+# Check if the attack is still active, before triggering an unban callback with this option
+# If the attack is still active, check each run of the unban watchdog
+unban_only_if_attack_finished = on
+
+# enable per subnet speed meters
+# For each subnet, list track speed in bps and pps for both directions
+enable_subnet_counters = off
+
+{% if "mirror" in mode %}
+mirror_afpacket = on
+{% endif -%}
+
+{% if "in" in direction %}
+process_incoming_traffic = on
+{% endif -%}
+{% if "out" in direction %}
+process_outgoing_traffic = on
+{% endif -%}
+{% for th in threshold %}
+{% if th == "fps" %}
+ban_for_flows = on
+threshold_flows = {{ threshold[th] }}
+{% endif -%}
+{% if th == "mbps" %}
+ban_for_bandwidth = on
+threshold_mbps = {{ threshold[th] }}
+{% endif -%}
+{% if th == "pps" %}
+ban_for_pps = on
+threshold_pps = {{ threshold[th] }}
+{% endif -%}
+{% endfor -%}
+
+{% if listen_interface %}
+{% set value = listen_interface if listen_interface is string else listen_interface | join(',') %}
+interfaces = {{ value }}
+{% endif -%}
+
+{% if alert_script %}
+notify_script_path = {{ alert_script }}
+{% endif -%}
diff --git a/data/templates/ids/fastnetmon_networks_list.tmpl b/data/templates/ids/fastnetmon_networks_list.tmpl
new file mode 100644
index 000000000..d58990053
--- /dev/null
+++ b/data/templates/ids/fastnetmon_networks_list.tmpl
@@ -0,0 +1,7 @@
+{% if network is string %}
+{{ network }}
+{% else %}
+{% for net in network %}
+{{ net }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/igmp-proxy/igmpproxy.conf.tmpl b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
new file mode 100644
index 000000000..c7fc5cef5
--- /dev/null
+++ b/data/templates/igmp-proxy/igmpproxy.conf.tmpl
@@ -0,0 +1,37 @@
+########################################################
+#
+# autogenerated by igmp_proxy.py
+#
+# The configuration file must define one upstream
+# interface, and one or more downstream interfaces.
+#
+# If multicast traffic originates outside the
+# upstream subnet, the "altnet" option can be
+# used in order to define legal multicast sources.
+# (Se example...)
+#
+# The "quickleave" should be used to avoid saturation
+# of the upstream link. The option should only
+# be used if it's absolutely nessecary to
+# accurately imitate just one Client.
+#
+########################################################
+
+{% if not disable_quickleave -%}
+quickleave
+{% endif -%}
+
+{% for interface in interfaces %}
+# Configuration for {{ interface.name }} ({{ interface.role }} interface)
+{% if interface.role == 'disabled' -%}
+phyint {{ interface.name }} disabled
+{%- else -%}
+phyint {{ interface.name }} {{ interface.role }} ratelimit 0 threshold {{ interface.threshold }}
+{%- endif -%}
+{%- for subnet in interface.alt_subnet %}
+ altnet {{ subnet }}
+{%- endfor %}
+{%- for subnet in interface.whitelist %}
+ whitelist {{ subnet }}
+{%- endfor %}
+{% endfor %}
diff --git a/data/templates/ipsec/charon.tmpl b/data/templates/ipsec/charon.tmpl
new file mode 100644
index 000000000..4d710921e
--- /dev/null
+++ b/data/templates/ipsec/charon.tmpl
@@ -0,0 +1,342 @@
+# Options for the charon IKE daemon.
+charon {
+
+ # Accept unencrypted ID and HASH payloads in IKEv1 Main Mode.
+ # accept_unencrypted_mainmode_messages = no
+
+ # Maximum number of half-open IKE_SAs for a single peer IP.
+ # block_threshold = 5
+
+ # Whether Certicate Revocation Lists (CRLs) fetched via HTTP or LDAP should
+ # be saved under a unique file name derived from the public key of the
+ # Certification Authority (CA) to /etc/ipsec.d/crls (stroke) or
+ # /etc/swanctl/x509crl (vici), respectively.
+ # cache_crls = no
+
+ # Whether relations in validated certificate chains should be cached in
+ # memory.
+ # cert_cache = yes
+
+ # Send Cisco Unity vendor ID payload (IKEv1 only).
+ # cisco_unity = no
+
+ # Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
+ # close_ike_on_child_failure = no
+
+ # Number of half-open IKE_SAs that activate the cookie mechanism.
+ # cookie_threshold = 10
+
+ # Delete CHILD_SAs right after they got successfully rekeyed (IKEv1 only).
+ # delete_rekeyed = no
+
+ # Use ANSI X9.42 DH exponent size or optimum size matched to cryptographic
+ # strength.
+ # dh_exponent_ansi_x9_42 = yes
+
+ # Use RTLD_NOW with dlopen when loading plugins and IMV/IMCs to reveal
+ # missing symbols immediately.
+ # dlopen_use_rtld_now = no
+
+ # DNS server assigned to peer via configuration payload (CP).
+ # dns1 =
+
+ # DNS server assigned to peer via configuration payload (CP).
+ # dns2 =
+
+ # Enable Denial of Service protection using cookies and aggressiveness
+ # checks.
+ # dos_protection = yes
+
+ # Compliance with the errata for RFC 4753.
+ # ecp_x_coordinate_only = yes
+
+ # Free objects during authentication (might conflict with plugins).
+ # flush_auth_cfg = no
+
+ # Whether to follow IKEv2 redirects (RFC 5685).
+ # follow_redirects = yes
+
+ # Maximum size (complete IP datagram size in bytes) of a sent IKE fragment
+ # when using proprietary IKEv1 or standardized IKEv2 fragmentation, defaults
+ # to 1280 (use 0 for address family specific default values, which uses a
+ # lower value for IPv4). If specified this limit is used for both IPv4 and
+ # IPv6.
+ # fragment_size = 1280
+
+ # Name of the group the daemon changes to after startup.
+ # group =
+
+ # Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING).
+ # half_open_timeout = 30
+
+ # Enable hash and URL support.
+ # hash_and_url = no
+
+ # Allow IKEv1 Aggressive Mode with pre-shared keys as responder.
+ # i_dont_care_about_security_and_use_aggressive_mode_psk = no
+
+ # Whether to ignore the traffic selectors from the kernel's acquire events
+ # for IKEv2 connections (they are not used for IKEv1).
+ # ignore_acquire_ts = no
+
+ # A space-separated list of routing tables to be excluded from route
+ # lookups.
+ # ignore_routing_tables =
+
+ # Maximum number of IKE_SAs that can be established at the same time before
+ # new connection attempts are blocked.
+ # ikesa_limit = 0
+
+ # Number of exclusively locked segments in the hash table.
+ # ikesa_table_segments = 1
+
+ # Size of the IKE_SA hash table.
+ # ikesa_table_size = 1
+
+ # Whether to close IKE_SA if the only CHILD_SA closed due to inactivity.
+ # inactivity_close_ike = no
+
+ # Limit new connections based on the current number of half open IKE_SAs,
+ # see IKE_SA_INIT DROPPING in strongswan.conf(5).
+ # init_limit_half_open = 0
+
+ # Limit new connections based on the number of queued jobs.
+ # init_limit_job_load = 0
+
+ # Causes charon daemon to ignore IKE initiation requests.
+ # initiator_only = no
+
+ # Install routes into a separate routing table for established IPsec
+ # tunnels.
+ install_routes = {{ install_routes }}
+
+ # Install virtual IP addresses.
+ # install_virtual_ip = yes
+
+ # The name of the interface on which virtual IP addresses should be
+ # installed.
+ # install_virtual_ip_on =
+
+ # Check daemon, libstrongswan and plugin integrity at startup.
+ # integrity_test = no
+
+ # A comma-separated list of network interfaces that should be ignored, if
+ # interfaces_use is specified this option has no effect.
+ # interfaces_ignore =
+
+ # A comma-separated list of network interfaces that should be used by
+ # charon. All other interfaces are ignored.
+ # interfaces_use =
+
+ # NAT keep alive interval.
+ # keep_alive = 20s
+
+ # Plugins to load in the IKE daemon charon.
+ # load =
+
+ # Determine plugins to load via each plugin's load option.
+ # load_modular = no
+
+ # Initiate IKEv2 reauthentication with a make-before-break scheme.
+ # make_before_break = no
+
+ # Maximum number of IKEv1 phase 2 exchanges per IKE_SA to keep state about
+ # and track concurrently.
+ # max_ikev1_exchanges = 3
+
+ # Maximum packet size accepted by charon.
+ # max_packet = 10000
+
+ # Enable multiple authentication exchanges (RFC 4739).
+ # multiple_authentication = yes
+
+ # WINS servers assigned to peer via configuration payload (CP).
+ # nbns1 =
+
+ # WINS servers assigned to peer via configuration payload (CP).
+ # nbns2 =
+
+ # UDP port used locally. If set to 0 a random port will be allocated.
+ # port = 500
+
+ # UDP port used locally in case of NAT-T. If set to 0 a random port will be
+ # allocated. Has to be different from charon.port, otherwise a random port
+ # will be allocated.
+ # port_nat_t = 4500
+
+ # Prefer locally configured proposals for IKE/IPsec over supplied ones as
+ # responder (disabling this can avoid keying retries due to
+ # INVALID_KE_PAYLOAD notifies).
+ # prefer_configured_proposals = yes
+
+ # By default public IPv6 addresses are preferred over temporary ones (RFC
+ # 4941), to make connections more stable. Enable this option to reverse
+ # this.
+ # prefer_temporary_addrs = no
+
+ # Process RTM_NEWROUTE and RTM_DELROUTE events.
+ # process_route = yes
+
+ # Delay in ms for receiving packets, to simulate larger RTT.
+ # receive_delay = 0
+
+ # Delay request messages.
+ # receive_delay_request = yes
+
+ # Delay response messages.
+ # receive_delay_response = yes
+
+ # Specific IKEv2 message type to delay, 0 for any.
+ # receive_delay_type = 0
+
+ # Size of the AH/ESP replay window, in packets.
+ # replay_window = 32
+
+ # Base to use for calculating exponential back off, see IKEv2 RETRANSMISSION
+ # in strongswan.conf(5).
+ # retransmit_base = 1.8
+
+ # Timeout in seconds before sending first retransmit.
+ # retransmit_timeout = 4.0
+
+ # Number of times to retransmit a packet before giving up.
+ # retransmit_tries = 5
+
+ # Interval in seconds to use when retrying to initiate an IKE_SA (e.g. if
+ # DNS resolution failed), 0 to disable retries.
+ # retry_initiate_interval = 0
+
+ # Initiate CHILD_SA within existing IKE_SAs (always enabled for IKEv1).
+ # reuse_ikesa = yes
+
+ # Numerical routing table to install routes to.
+ # routing_table =
+
+ # Priority of the routing table.
+ # routing_table_prio =
+
+ # Delay in ms for sending packets, to simulate larger RTT.
+ # send_delay = 0
+
+ # Delay request messages.
+ # send_delay_request = yes
+
+ # Delay response messages.
+ # send_delay_response = yes
+
+ # Specific IKEv2 message type to delay, 0 for any.
+ # send_delay_type = 0
+
+ # Send strongSwan vendor ID payload
+ # send_vendor_id = no
+
+ # Whether to enable Signature Authentication as per RFC 7427.
+ # signature_authentication = yes
+
+ # Whether to enable constraints against IKEv2 signature schemes.
+ # signature_authentication_constraints = yes
+
+ # Number of worker threads in charon.
+ # threads = 16
+
+ # Name of the user the daemon changes to after startup.
+ # user =
+
+ crypto_test {
+
+ # Benchmark crypto algorithms and order them by efficiency.
+ # bench = no
+
+ # Buffer size used for crypto benchmark.
+ # bench_size = 1024
+
+ # Number of iterations to test each algorithm.
+ # bench_time = 50
+
+ # Test crypto algorithms during registration (requires test vectors
+ # provided by the test-vectors plugin).
+ # on_add = no
+
+ # Test crypto algorithms on each crypto primitive instantiation.
+ # on_create = no
+
+ # Strictly require at least one test vector to enable an algorithm.
+ # required = no
+
+ # Whether to test RNG with TRUE quality; requires a lot of entropy.
+ # rng_true = no
+
+ }
+
+ host_resolver {
+
+ # Maximum number of concurrent resolver threads (they are terminated if
+ # unused).
+ # max_threads = 3
+
+ # Minimum number of resolver threads to keep around.
+ # min_threads = 0
+
+ }
+
+ leak_detective {
+
+ # Includes source file names and line numbers in leak detective output.
+ # detailed = yes
+
+ # Threshold in bytes for leaks to be reported (0 to report all).
+ # usage_threshold = 10240
+
+ # Threshold in number of allocations for leaks to be reported (0 to
+ # report all).
+ # usage_threshold_count = 0
+
+ }
+
+ processor {
+
+ # Section to configure the number of reserved threads per priority class
+ # see JOB PRIORITY MANAGEMENT in strongswan.conf(5).
+ priority_threads {
+
+ }
+
+ }
+
+ # Section containing a list of scripts (name = path) that are executed when
+ # the daemon is started.
+ start-scripts {
+
+ }
+
+ # Section containing a list of scripts (name = path) that are executed when
+ # the daemon is terminated.
+ stop-scripts {
+
+ }
+
+ tls {
+
+ # List of TLS encryption ciphers.
+ # cipher =
+
+ # List of TLS key exchange methods.
+ # key_exchange =
+
+ # List of TLS MAC algorithms.
+ # mac =
+
+ # List of TLS cipher suites.
+ # suites =
+
+ }
+
+ x509 {
+
+ # Discard certificates with unsupported or unknown critical extensions.
+ # enforce_critical = yes
+
+ }
+
+}
+
diff --git a/data/templates/ipsec/ipsec.conf.tmpl b/data/templates/ipsec/ipsec.conf.tmpl
new file mode 100644
index 000000000..d0b60765b
--- /dev/null
+++ b/data/templates/ipsec/ipsec.conf.tmpl
@@ -0,0 +1,3 @@
+{{delim_ipsec_l2tp_begin}}
+include {{ipsec_ra_conn_file}}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/ipsec/ipsec.secrets.tmpl b/data/templates/ipsec/ipsec.secrets.tmpl
new file mode 100644
index 000000000..55c010a3b
--- /dev/null
+++ b/data/templates/ipsec/ipsec.secrets.tmpl
@@ -0,0 +1,7 @@
+{{delim_ipsec_l2tp_begin}}
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+{{outside_addr}} %any : PSK "{{ipsec_l2tp_secret}}"
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+: RSA {{server_key_file_copied}}
+{% endif%}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/ipsec/remote-access.tmpl b/data/templates/ipsec/remote-access.tmpl
new file mode 100644
index 000000000..fae48232f
--- /dev/null
+++ b/data/templates/ipsec/remote-access.tmpl
@@ -0,0 +1,28 @@
+{{delim_ipsec_l2tp_begin}}
+conn {{ra_conn_name}}
+ type=transport
+ left={{outside_addr}}
+ leftsubnet=%dynamic[/1701]
+ rightsubnet=%dynamic
+ mark_in=%unique
+ auto=add
+ ike=aes256-sha1-modp1024,3des-sha1-modp1024,3des-sha1-modp1024!
+ dpddelay=15
+ dpdtimeout=45
+ dpdaction=clear
+ esp=aes256-sha1,3des-sha1!
+ rekey=no
+{% if ipsec_l2tp_auth_mode == 'pre-shared-secret' %}
+ authby=secret
+ leftauth=psk
+ rightauth=psk
+{% elif ipsec_l2tp_auth_mode == 'x509' %}
+ authby=rsasig
+ leftrsasigkey=%cert
+ rightrsasigkey=%cert
+ rightca=%same
+ leftcert={{server_cert_file_copied}}
+{% endif %}
+ ikelifetime={{ipsec_l2tp_ike_lifetime}}
+ keylife={{ipsec_l2tp_lifetime}}
+{{delim_ipsec_l2tp_end}}
diff --git a/data/templates/lcd/LCDd.conf.tmpl b/data/templates/lcd/LCDd.conf.tmpl
new file mode 100644
index 000000000..6cf6a440f
--- /dev/null
+++ b/data/templates/lcd/LCDd.conf.tmpl
@@ -0,0 +1,132 @@
+### Autogenerted by system-display.py ##
+
+# LCDd.conf -- configuration file for the LCDproc server daemon LCDd
+#
+# This file contains the configuration for the LCDd server.
+#
+# The format is ini-file-like. It is divided into sections that start at
+# markers that look like [section]. Comments are all line-based comments,
+# and are lines that start with '#' or ';'.
+#
+# The server has a 'central' section named [server]. For the menu there is
+# a section called [menu]. Further each driver has a section which
+# defines how the driver acts.
+#
+# The drivers are activated by specifying them in a driver= line in the
+# server section, like:
+#
+# Driver=curses
+#
+# This tells LCDd to use the curses driver.
+# The first driver that is loaded and is capable of output defines the
+# size of the display. The default driver to use is curses.
+# If the driver is specified using the -d <driver> command line option,
+# the Driver= options in the config file are ignored.
+#
+# The drivers read their own options from the respective sections.
+
+## Server section with all kinds of settings for the LCDd server ##
+[server]
+
+# Where can we find the driver modules ?
+# NOTE: Always place a slash as last character !
+DriverPath=/usr/lib/x86_64-linux-gnu/lcdproc/
+
+# Tells the server to load the given drivers. Multiple lines can be given.
+# The name of the driver is case sensitive and determines the section
+# where to look for further configuration options of the specific driver
+# as well as the name of the dynamic driver module to load at runtime.
+# The latter one can be changed by giving a File= directive in the
+# driver specific section.
+#
+# The following drivers are supported:
+# bayrad, CFontz, CFontzPacket, curses, CwLnx, ea65, EyeboxOne, futaba,
+# g15, glcd, glcdlib, glk, hd44780, icp_a106, imon, imonlcd,, IOWarrior,
+# irman, joy, lb216, lcdm001, lcterm, linux_input, lirc, lis, MD8800,
+# mdm166a, ms6931, mtc_s16209x, MtxOrb, mx5000, NoritakeVFD,
+# Olimex_MOD_LCD1x9, picolcd, pyramid, rawserial, sdeclcd, sed1330,
+# sed1520, serialPOS, serialVFD, shuttleVFD, sli, stv5730, svga, t6963,
+# text, tyan, ula200, vlsys_m428, xosd, yard2LCD
+
+{% if model is defined %}
+{% if model.startswith('cfa-') %}
+Driver=CFontzPacket
+{% elif model == 'sdec' %}
+Driver=sdeclcd
+{% endif %}
+{% endif %}
+
+# Tells the driver to bind to the given interface. [default: 127.0.0.1]
+Bind=127.0.0.1
+
+# Listen on this specified port. [default: 13666]
+Port=13666
+
+# Sets the reporting level; defaults to warnings and errors only.
+# [default: 2; legal: 0-5]
+ReportLevel=3
+
+# Should we report to syslog instead of stderr? [default: no; legal: yes, no]
+ReportToSyslog=yes
+
+# User to run as. LCDd will drop its root privileges and run as this user
+# instead. [default: nobody]
+User=nobody
+
+# The server will stay in the foreground if set to yes.
+# [default: no, legal: yes, no]
+Foreground=yes
+
+# Hello message: each entry represents a display line; default: builtin
+Hello="Starting VyOS..."
+
+# GoodBye message: each entry represents a display line; default: builtin
+GoodBye="VyOS shutdown..."
+
+# Sets the interval in microseconds for updating the display.
+# [default: 125000 meaning 8Hz]
+FrameInterval=500000 # 2 updates per second
+
+# Sets the default time in seconds to displays a screen. [default: 4]
+WaitTime=1
+
+# If set to no, LCDd will start with screen rotation disabled. This has the
+# same effect as if the ToggleRotateKey had been pressed. Rotation will start
+# if the ToggleRotateKey is pressed. Note that this setting does not turn off
+# priority sorting of screens. [default: on; legal: on, off]
+AutoRotate=on
+
+# If yes, the the serverscreen will be rotated as a usual info screen. If no,
+# it will be a background screen, only visible when no other screens are
+# active. The special value 'blank' is similar to no, but only a blank screen
+# is displayed. [default: on; legal: on, off, blank]
+ServerScreen=blank
+
+# Set master backlight setting. If set to 'open' a client may control the
+# backlight for its own screens (only). [default: open; legal: off, open, on]
+Backlight=on
+
+# Set master heartbeat setting. If set to 'open' a client may control the
+# heartbeat for its own screens (only). [default: open; legal: off, open, on]
+Heartbeat=off
+
+# set title scrolling speed [default: 10; legal: 0-10]
+TitleSpeed=10
+
+{% if model is defined and model is not none %}
+{% if model.startswith('cfa-') %}
+## CrystalFontz packet driver (for CFA533, CFA631, CFA633 & CFA635) ##
+[CFontzPacket]
+Model={{ model.split('-')[1] }}
+Device={{ device }}
+Contrast=350
+Brightness=500
+OffBrightness=50
+Reboot=yes
+USB=yes
+{% elif model == 'sdec' %}
+## SDEC driver for Lanner, Watchguard, Sophos sppliances ##
+[sdeclcd]
+# No options
+{% endif %}
+{% endif %}
diff --git a/data/templates/lcd/lcdproc.conf.tmpl b/data/templates/lcd/lcdproc.conf.tmpl
new file mode 100644
index 000000000..c79f3cd0d
--- /dev/null
+++ b/data/templates/lcd/lcdproc.conf.tmpl
@@ -0,0 +1,60 @@
+### autogenerated by system-lcd.py ###
+
+# LCDproc client configuration file
+
+[lcdproc]
+Server=127.0.0.1
+Port=13666
+
+# set reporting level
+ReportLevel=3
+
+# report to to syslog ?
+ReportToSyslog=true
+
+Foreground=yes
+
+[CPU]
+Active=true
+OnTime=1
+OffTime=2
+ShowInvisible=false
+
+[SMP-CPU]
+Active=false
+
+[Memory]
+Active=false
+
+[Load]
+Active=false
+
+[Uptime]
+Active=true
+
+[ProcSize]
+Active=false
+
+[Disk]
+Active=false
+
+[About]
+Active=false
+
+[TimeDate]
+Active=true
+TimeFormat="%H:%M:%S"
+
+[OldTime]
+Active=false
+
+[BigClock]
+Active=false
+
+[MiniClock]
+Active=false
+
+# Display the title bar in two-line mode. Note that with four lines or more
+# the title is always shown. [default: true; legal: true, false]
+ShowTitle=false
+
diff --git a/data/templates/lldp/lldpd.tmpl b/data/templates/lldp/lldpd.tmpl
new file mode 100644
index 000000000..3db955b48
--- /dev/null
+++ b/data/templates/lldp/lldpd.tmpl
@@ -0,0 +1,3 @@
+### Autogenerated by lldp.py ###
+DAEMON_ARGS="-M 4{% if options.snmp %} -x{% endif %}{% if options.cdp %} -c{% endif %}{% if options.edp %} -e{% endif %}{% if options.fdp %} -f{% endif %}{% if options.sonmp %} -s{% endif %}"
+
diff --git a/data/templates/lldp/vyos.conf.tmpl b/data/templates/lldp/vyos.conf.tmpl
new file mode 100644
index 000000000..e724f42c6
--- /dev/null
+++ b/data/templates/lldp/vyos.conf.tmpl
@@ -0,0 +1,20 @@
+### Autogenerated by lldp.py ###
+
+configure system platform VyOS
+configure system description "VyOS {{ options.description }}"
+{% if options.listen_on -%}
+configure system interface pattern "{{ ( options.listen_on | select('equalto','all') | map('replace','all','*') | list + options.listen_on | select('equalto','!all') | map('replace','!all','!*') | list + options.listen_on | reject('equalto','all') | reject('equalto','!all') | list ) | unique | join(",") }}"
+{%- endif %}
+{% if options.mgmt_addr -%}
+configure system ip management pattern {{ options.mgmt_addr | join(",") }}
+{%- endif %}
+{%- for loc in location -%}
+{%- if loc.elin %}
+configure ports {{ loc.name }} med location elin "{{ loc.elin }}"
+{%- endif %}
+{%- if loc.coordinate_based %}
+configure ports {{ loc.name }} med location coordinate {% if loc.coordinate_based.latitude %}latitude {{ loc.coordinate_based.latitude }}{% endif %} {% if loc.coordinate_based.longitude %}longitude {{ loc.coordinate_based.longitude }}{% endif %} {% if loc.coordinate_based.altitude %}altitude {{ loc.coordinate_based.altitude }} m{% endif %} {% if loc.coordinate_based.datum %}datum {{ loc.coordinate_based.datum }}{% endif %}
+{%- endif %}
+
+
+{% endfor %}
diff --git a/data/templates/macsec/wpa_supplicant.conf.tmpl b/data/templates/macsec/wpa_supplicant.conf.tmpl
new file mode 100644
index 000000000..1731bf160
--- /dev/null
+++ b/data/templates/macsec/wpa_supplicant.conf.tmpl
@@ -0,0 +1,89 @@
+# autogenerated by interfaces-macsec.py
+
+# see full documentation:
+# https://w1.fi/cgit/hostap/plain/wpa_supplicant/wpa_supplicant.conf
+
+# For UNIX domain sockets (default on Linux and BSD): This is a directory that
+# will be created for UNIX domain sockets for listening to requests from
+# external programs (CLI/GUI, etc.) for status information and configuration.
+# The socket file will be named based on the interface name, so multiple
+# wpa_supplicant processes can be run at the same time if more than one
+# interface is used.
+# /var/run/wpa_supplicant is the recommended directory for sockets and by
+# default, wpa_cli will use it when trying to connect with wpa_supplicant.
+ctrl_interface=/run/wpa_supplicant
+
+# Note: When using MACsec, eapol_version shall be set to 3, which is
+# defined in IEEE Std 802.1X-2010.
+eapol_version=3
+
+# No need to scan for access points in MACsec mode
+ap_scan=0
+
+# EAP fast re-authentication
+fast_reauth=1
+
+network={
+ key_mgmt=NONE
+
+ # Note: When using wired authentication (including MACsec drivers),
+ # eapol_flags must be set to 0 for the authentication to be completed
+ # successfully.
+ eapol_flags=0
+
+ # macsec_policy: IEEE 802.1X/MACsec options
+ # This determines how sessions are secured with MACsec (only for MACsec
+ # drivers).
+ # 0: MACsec not in use (default)
+ # 1: MACsec enabled - Should secure, accept key server's advice to
+ # determine whether to use a secure session or not.
+ macsec_policy=1
+
+ # macsec_integ_only: IEEE 802.1X/MACsec transmit mode
+ # This setting applies only when MACsec is in use, i.e.,
+ # - macsec_policy is enabled
+ # - the key server has decided to enable MACsec
+ # 0: Encrypt traffic (default)
+ # 1: Integrity only
+ macsec_integ_only={{ '0' if security is defined and security.encrypt is defined else '1' }}
+
+{% if security is defined %}
+{% if security.encrypt is defined %}
+ # mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
+ # This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
+ # In this mode, instances of wpa_supplicant can act as MACsec peers. The peer
+ # with lower priority will become the key server and start distributing SAKs.
+ # mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit)
+ # hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits)
+ # mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string
+ # (2..64 hex-digits)
+ mka_cak={{ security.mka.cak }}
+ mka_ckn={{ security.mka.ckn }}
+
+ # mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
+ # default priority
+ mka_priority={{ security.mka.priority }}
+{% endif %}
+
+{% if security.replay_window is defined %}
+ # macsec_replay_protect: IEEE 802.1X/MACsec replay protection
+ # This setting applies only when MACsec is in use, i.e.,
+ # - macsec_policy is enabled
+ # - the key server has decided to enable MACsec
+ # 0: Replay protection disabled (default)
+ # 1: Replay protection enabled
+ macsec_replay_protect=1
+
+ # macsec_replay_window: IEEE 802.1X/MACsec replay protection window
+ # This determines a window in which replay is tolerated, to allow receipt
+ # of frames that have been misordered by the network.
+ # This setting applies only when MACsec replay protection active, i.e.,
+ # - macsec_replay_protect is enabled
+ # - the key server has decided to enable MACsec
+ # 0: No replay window, strict check (default)
+ # 1..2^32-1: number of packets that could be misordered
+ macsec_replay_window={{ security.replay_window }}
+{% endif %}
+{% endif %}
+}
+
diff --git a/data/templates/mdns-repeater/mdns-repeater.tmpl b/data/templates/mdns-repeater/mdns-repeater.tmpl
new file mode 100644
index 000000000..80f4ab047
--- /dev/null
+++ b/data/templates/mdns-repeater/mdns-repeater.tmpl
@@ -0,0 +1,2 @@
+### Autogenerated by mdns_repeater.py ###
+DAEMON_ARGS="{{ interface | join(' ') }}"
diff --git a/data/templates/netflow/uacctd.conf.tmpl b/data/templates/netflow/uacctd.conf.tmpl
new file mode 100644
index 000000000..d8615566f
--- /dev/null
+++ b/data/templates/netflow/uacctd.conf.tmpl
@@ -0,0 +1,69 @@
+# Genereated from VyOS configuration
+daemonize: true
+promisc: false
+pidfile: /var/run/uacctd.pid
+uacctd_group: 2
+uacctd_nl_size: 2097152
+snaplen: {{ snaplen }}
+aggregate: in_iface,src_mac,dst_mac,vlan,src_host,dst_host,src_port,dst_port,proto,tos,flows
+plugin_pipe_size: {{ templatecfg['plugin_pipe_size'] }}
+plugin_buffer_size: {{ templatecfg['plugin_buffer_size'] }}
+{%- if templatecfg['syslog-facility'] != none %}
+syslog: {{ templatecfg['syslog-facility'] }}
+{%- endif %}
+{%- if templatecfg['disable-imt'] == none %}
+imt_path: /tmp/uacctd.pipe
+imt_mem_pools_number: 169
+{%- endif %}
+plugins:
+{%- if templatecfg['netflow']['servers'] != none -%}
+ {% for server in templatecfg['netflow']['servers'] %}
+ {%- if loop.last -%}nfprobe[nf_{{ server['address'] }}]{%- else %}nfprobe[nf_{{ server['address'] }}],{%- endif %}
+ {%- endfor -%}
+ {% set plugins_presented = true %}
+{%- endif %}
+{%- if templatecfg['sflow']['servers'] != none -%}
+ {% if plugins_presented -%}
+ {%- for server in templatecfg['sflow']['servers'] -%}
+ ,sfprobe[sf_{{ server['address'] }}]
+ {%- endfor %}
+ {%- else %}
+ {%- for server in templatecfg['sflow']['servers'] %}
+ {%- if loop.last -%}sfprobe[sf_{{ server['address'] }}]{%- else %}sfprobe[sf_{{ server['address'] }}],{%- endif %}
+ {%- endfor %}
+ {%- endif -%}
+ {% set plugins_presented = true %}
+{%- endif %}
+{%- if templatecfg['disable-imt'] == none %}
+ {%- if plugins_presented -%},memory{%- else %}memory{%- endif %}
+{%- endif %}
+{%- if templatecfg['netflow']['servers'] != none %}
+{%- for server in templatecfg['netflow']['servers'] %}
+nfprobe_receiver[nf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
+nfprobe_version[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['version'] }}
+{%- if templatecfg['netflow']['engine-id'] != none %}
+nfprobe_engine[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['engine-id'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['max-flows'] != none %}
+nfprobe_maxflows[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['max-flows'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['sampling-rate'] != none %}
+sampling_rate[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['sampling-rate'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['source-ip'] != none %}
+nfprobe_source_ip[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['source-ip'] }}
+{%- endif %}
+{%- if templatecfg['netflow']['timeout_string'] != '' %}
+nfprobe_timeouts[nf_{{ server['address'] }}]: {{ templatecfg['netflow']['timeout_string'] }}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+{%- if templatecfg['sflow']['servers'] != none %}
+{%- for server in templatecfg['sflow']['servers'] %}
+sfprobe_receiver[sf_{{ server['address'] }}]: {{ server['address'] }}:{{ server['port'] }}
+sfprobe_agentip[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['agent-address'] }}
+{%- if templatecfg['sflow']['sampling-rate'] != none %}
+sampling_rate[sf_{{ server['address'] }}]: {{ templatecfg['sflow']['sampling-rate'] }}
+{%- endif %}
+{%- endfor %}
+{% endif %}
diff --git a/data/templates/ntp/ntp.conf.tmpl b/data/templates/ntp/ntp.conf.tmpl
new file mode 100644
index 000000000..6ef0c0f2c
--- /dev/null
+++ b/data/templates/ntp/ntp.conf.tmpl
@@ -0,0 +1,47 @@
+### Autogenerated by ntp.py ###
+
+#
+# Non-configurable defaults
+#
+driftfile /var/lib/ntp/ntp.drift
+# By default, only allow ntpd to query time sources, ignore any incoming requests
+restrict default noquery nopeer notrap nomodify
+# Local users have unrestricted access, allowing reconfiguration via ntpdc
+restrict 127.0.0.1
+restrict -6 ::1
+
+#
+# Configurable section
+#
+{% if server %}
+{% for srv in server %}
+{% set options = '' %}
+{% set options = options + 'noselect ' if server[srv].noselect is defined else '' %}
+{% set options = options + 'preempt ' if server[srv].preempt is defined else '' %}
+{% set options = options + 'prefer ' if server[srv].prefer is defined else '' %}
+server {{ srv | replace('_', '-') }} iburst {{ options }}
+{% endfor %}
+{% endif %}
+
+{% if allow_clients is defined and allow_clients.address is defined %}
+# Allowed clients configuration
+{% if allow_clients.address is string %}
+restrict {{ allow_clients.address|address_from_cidr }} mask {{ allow_clients.address|netmask_from_cidr }} nomodify notrap nopeer
+{% else %}
+{% for address in allow_clients.address %}
+restrict {{ address|address_from_cidr }} mask {{ address|netmask_from_cidr }} nomodify notrap nopeer
+{% endfor %}
+{% endif %}
+{% endif %}
+
+{% if listen_address %}
+# NTP should listen on configured addresses only
+interface ignore wildcard
+{% if listen_address is string %}
+interface listen {{ listen_address }}
+{% else %}
+{% for address in listen_address %}
+interface listen {{ address }}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/data/templates/ntp/override.conf.tmpl b/data/templates/ntp/override.conf.tmpl
new file mode 100644
index 000000000..466638e5a
--- /dev/null
+++ b/data/templates/ntp/override.conf.tmpl
@@ -0,0 +1,11 @@
+{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+
+[Service]
+ExecStart=
+ExecStart={{vrf_command}}/usr/lib/ntp/ntp-systemd-wrapper
+Restart=on-failure
+RestartSec=10
+
diff --git a/data/templates/ocserv/ocserv_config.tmpl b/data/templates/ocserv/ocserv_config.tmpl
new file mode 100644
index 000000000..6aaeff693
--- /dev/null
+++ b/data/templates/ocserv/ocserv_config.tmpl
@@ -0,0 +1,82 @@
+### generated by vpn_anyconnect.py ###
+
+tcp-port = {{ listen_ports.tcp }}
+udp-port = {{ listen_ports.udp }}
+
+run-as-user = nobody
+run-as-group = daemon
+
+{% if "radius" in authentication.mode %}
+auth = "radius [config=/run/ocserv/radiusclient.conf]"
+{% else %}
+auth = "plain[/run/ocserv/ocpasswd]"
+{% endif %}
+
+{% if ssl.cert_file %}
+server-cert = {{ ssl.cert_file }}
+{% endif %}
+
+{% if ssl.key_file %}
+server-key = {{ ssl.key_file }}
+{% endif %}
+
+{% if ssl.ca_cert_file %}
+ca-cert = {{ ssl.ca_cert_file }}
+{% endif %}
+
+socket-file = /run/ocserv/ocserv.socket
+occtl-socket-file = /run/ocserv/occtl.socket
+use-occtl = true
+isolate-workers = true
+keepalive = 300
+dpd = 60
+mobile-dpd = 300
+switch-to-tcp-timeout = 30
+tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128"
+auth-timeout = 240
+idle-timeout = 1200
+mobile-idle-timeout = 1800
+min-reauth-time = 3
+cookie-timeout = 300
+rekey-method = ssl
+try-mtu-discovery = true
+cisco-client-compat = true
+dtls-legacy = true
+
+
+# The name to use for the tun device
+device = sslvpn
+
+# An alternative way of specifying the network:
+{% if network_settings %}
+# DNS settings
+{% if network_settings.name_server is string %}
+dns = {{ network_settings.name_server }}
+{% else %}
+{% for dns in network_settings.name_server %}
+dns = {{ dns }}
+{% endfor %}
+{% endif %}
+# IPv4 network pool
+{% if network_settings.client_ip_settings %}
+{% if network_settings.client_ip_settings.subnet %}
+ipv4-network = {{ network_settings.client_ip_settings.subnet }}
+{% endif %}
+{% endif %}
+# IPv6 network pool
+{% if network_settings.client_ipv6_pool %}
+{% if network_settings.client_ipv6_pool.prefix %}
+ipv6-network = {{ network_settings.client_ipv6_pool.prefix }}
+ipv6-subnet-prefix = {{ network_settings.client_ipv6_pool.mask }}
+{% endif %}
+{% endif %}
+{% endif %}
+
+{% if network_settings.push_route is string %}
+route = {{ network_settings.push_route }}
+{% else %}
+{% for route in network_settings.push_route %}
+route = {{ route }}
+{% endfor %}
+{% endif %}
+
diff --git a/data/templates/ocserv/ocserv_passwd.tmpl b/data/templates/ocserv/ocserv_passwd.tmpl
new file mode 100644
index 000000000..ffadb4860
--- /dev/null
+++ b/data/templates/ocserv/ocserv_passwd.tmpl
@@ -0,0 +1,6 @@
+#<username>:<group>:<hash>
+{% for user in username if username is defined %}
+{% if not "disable" in username[user] %}
+{{ user }}:*:{{ username[user].hash }}
+{% endif %}
+{% endfor %} \ No newline at end of file
diff --git a/data/templates/ocserv/radius_conf.tmpl b/data/templates/ocserv/radius_conf.tmpl
new file mode 100644
index 000000000..2d19306a0
--- /dev/null
+++ b/data/templates/ocserv/radius_conf.tmpl
@@ -0,0 +1,22 @@
+### generated by cpn_anyconnect.py ###
+nas-identifier VyOS
+{% for srv in server %}
+{% if not "disable" in server[srv] %}
+{% if "port" in server[srv] %}
+authserver {{ srv }}:{{server[srv]["port"]}}
+{% else %}
+authserver {{ srv }}
+{% endif %}
+{% endif %}
+{% endfor %}
+radius_timeout {{ timeout }}
+{% if source_address %}
+bindaddr {{ source_address }}
+{% else %}
+bindaddr *
+{% endif %}
+servers /run/ocserv/radius_servers
+dictionary /etc/radcli/dictionary
+default_realm
+radius_retries 3
+# \ No newline at end of file
diff --git a/data/templates/ocserv/radius_servers.tmpl b/data/templates/ocserv/radius_servers.tmpl
new file mode 100644
index 000000000..ba21fa074
--- /dev/null
+++ b/data/templates/ocserv/radius_servers.tmpl
@@ -0,0 +1,7 @@
+### generated by cpn_anyconnect.py ###
+# server key
+{% for srv in server %}
+{% if not "disable" in server[srv] %}
+{{ srv }} {{ server[srv].key }}
+{% endif %}
+{% endfor %}
diff --git a/data/templates/openvpn/client.conf.tmpl b/data/templates/openvpn/client.conf.tmpl
new file mode 100644
index 000000000..508d8da94
--- /dev/null
+++ b/data/templates/openvpn/client.conf.tmpl
@@ -0,0 +1,35 @@
+### Autogenerated by interfaces-openvpn.py ###
+
+{% if ip -%}
+ifconfig-push {{ ip[0] }} {{ remote_netmask }}
+{% endif -%}
+
+{% for route in push_route -%}
+push "route {{ route }}"
+{% endfor -%}
+
+{% for net in subnet -%}
+iroute {{ net }}
+{% endfor -%}
+
+{# ipv6_remote is only set when IPv6 server is enabled #}
+{% if ipv6_remote -%}
+# IPv6
+
+{%- if ipv6_ip %}
+ifconfig-ipv6-push {{ ipv6_ip[0] }} {{ ipv6_remote }}
+{%- endif %}
+
+{%- for route6 in ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for net6 in ipv6_subnet %}
+iroute {{ net6 }}
+{%- endfor %}
+
+{% endif -%}
+
+{% if disable -%}
+disable
+{% endif -%}
diff --git a/data/templates/openvpn/server.conf.tmpl b/data/templates/openvpn/server.conf.tmpl
new file mode 100644
index 000000000..401f8e04b
--- /dev/null
+++ b/data/templates/openvpn/server.conf.tmpl
@@ -0,0 +1,262 @@
+### Autogenerated by interfaces-openvpn.py ###
+#
+# See https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
+# for individual keyword definition
+
+{% if description -%}
+# {{ description }}
+
+{% endif -%}
+
+verb 3
+
+user {{ uid }}
+group {{ gid }}
+
+dev-type {{ type }}
+dev {{ intf }}
+persist-key
+iproute /usr/libexec/vyos/system/unpriv-ip
+
+proto {{ protocol_real }}
+
+{%- if local_host %}
+local {{ local_host }}
+{%- endif %}
+
+{%- if mode == 'server' and protocol == 'udp' and not local_host %}
+multihome
+{%- endif %}
+
+{%- if local_port %}
+lport {{ local_port }}
+{%- endif %}
+
+{% if remote_port -%}
+rport {{ remote_port }}
+{% endif %}
+
+{%- if remote_host %}
+{%- for remote in remote_host -%}
+remote {{ remote }}
+{% endfor -%}
+{% endif -%}
+
+{% if shared_secret_file %}
+secret {{ shared_secret_file }}
+{%- endif %}
+
+{%- if persistent_tunnel %}
+persist-tun
+{%- endif %}
+
+{%- if redirect_gateway %}
+push "redirect-gateway {{ redirect_gateway }}"
+{%- endif %}
+
+{%- if compress_lzo %}
+compress lzo
+{%- endif %}
+
+{% if 'client' in mode -%}
+#
+# OpenVPN Client mode
+#
+client
+nobind
+
+{% elif 'server' in mode -%}
+#
+# OpenVPN Server mode
+#
+
+{%- if server_topology %}
+topology {% if server_topology == 'point-to-point' %}p2p{% else %}{{ server_topology }}{% endif %}
+{%- endif %}
+
+{%- if is_bridge_member %}
+mode server
+tls-server
+{%- else %}
+server {{ server_subnet[0] }} nopool
+{%- endif %}
+
+{%- if server_pool %}
+ifconfig-pool {{ server_pool_start }} {{ server_pool_stop }}{% if server_pool_netmask %} {{ server_pool_netmask }}{% endif %}
+{%- endif %}
+
+{%- if server_max_conn %}
+max-clients {{ server_max_conn }}
+{%- endif %}
+
+{%- if client %}
+client-config-dir /run/openvpn/ccd/{{ intf }}
+{%- endif %}
+
+{%- if server_reject_unconfigured %}
+ccd-exclusive
+{%- endif %}
+
+keepalive {{ ping_interval }} {{ ping_restart }}
+management /run/openvpn/openvpn-mgmt-intf unix
+
+{% for route in server_push_route -%}
+push "route {{ route }}"
+{% endfor -%}
+
+{% for ns in server_dns_nameserver -%}
+push "dhcp-option DNS {{ ns }}"
+{% endfor -%}
+
+{%- if server_domain -%}
+push "dhcp-option DOMAIN {{ server_domain }}"
+{% endif -%}
+
+{%- if server_ipv6_local %}
+# IPv6
+push "tun-ipv6"
+ifconfig-ipv6 {{ server_ipv6_local }}/{{ server_ipv6_prefixlen }} {{ server_ipv6_remote }}
+
+{%- if server_ipv6_pool %}
+ifconfig-ipv6-pool {{ server_ipv6_pool_base }}/{{ server_ipv6_pool_prefixlen }}
+{%- endif %}
+
+{%- for route6 in server_ipv6_push_route %}
+push "route-ipv6 {{ route6 }}"
+{%- endfor %}
+
+{%- for ns6 in server_ipv6_dns_nameserver %}
+push "dhcp-option DNS6 {{ ns6 }}"
+{%- endfor %}
+
+{%- endif %}
+
+{% else -%}
+#
+# OpenVPN site-2-site mode
+#
+ping {{ ping_interval }}
+ping-restart {{ ping_restart }}
+
+{% if local_address_subnet -%}
+ifconfig {{ local_address[0] }} {{ local_address_subnet }}
+{%- elif remote_address -%}
+ifconfig {{ local_address[0] }} {{ remote_address[0] }}
+{%- endif %}
+
+{% if ipv6_local_address -%}
+ifconfig-ipv6 {{ ipv6_local_address[0] }} {{ ipv6_remote_address[0] }}
+{%- endif %}
+
+{% endif -%}
+
+{% if tls -%}
+# TLS options
+{%- if tls_ca_cert %}
+ca {{ tls_ca_cert }}
+{%- endif %}
+
+{%- if tls_cert %}
+cert {{ tls_cert }}
+{%- endif %}
+
+{%- if tls_key %}
+key {{ tls_key }}
+{%- endif %}
+
+{%- if tls_crypt %}
+tls-crypt {{ tls_crypt }}
+{%- endif %}
+
+{%- if tls_crl %}
+crl-verify {{ tls_crl }}
+{%- endif %}
+
+{%- if tls_version_min %}
+tls-version-min {{tls_version_min}}
+{%- endif %}
+
+{%- if tls_dh %}
+dh {{ tls_dh }}
+{%- endif %}
+
+{%- if tls_auth %}
+tls-auth {{tls_auth}}
+{%- endif %}
+
+{%- if tls_role %}
+{%- if 'active' in tls_role %}
+tls-client
+{%- elif 'passive' in tls_role %}
+tls-server
+{%- endif %}
+{%- endif %}
+
+{%- endif %}
+
+# Encryption options
+{%- if encryption %}
+{% if encryption == 'des' -%}
+cipher des-cbc
+{%- elif encryption == '3des' -%}
+cipher des-ede3-cbc
+{%- elif encryption == 'bf128' -%}
+cipher bf-cbc
+keysize 128
+{%- elif encryption == 'bf256' -%}
+cipher bf-cbc
+keysize 25
+{%- elif encryption == 'aes128gcm' -%}
+cipher aes-128-gcm
+{%- elif encryption == 'aes128' -%}
+cipher aes-128-cbc
+{%- elif encryption == 'aes192gcm' -%}
+cipher aes-192-gcm
+{%- elif encryption == 'aes192' -%}
+cipher aes-192-cbc
+{%- elif encryption == 'aes256gcm' -%}
+cipher aes-256-gcm
+{%- elif encryption == 'aes256' -%}
+cipher aes-256-cbc
+{%- endif -%}
+{%- endif %}
+
+{%- if ncp_ciphers %}
+ncp-ciphers {{ncp_ciphers}}
+{%- endif %}
+{%- if disable_ncp %}
+ncp-disable
+{%- endif %}
+
+{% if hash -%}
+auth {{ hash }}
+{%- endif -%}
+
+{%- if auth %}
+auth-user-pass {{ auth_user_pass_file }}
+auth-retry nointeract
+{%- endif %}
+
+# DEPRECATED This option will be removed in OpenVPN 2.5
+# Until OpenVPN v2.3 the format of the X.509 Subject fields was formatted like this:
+# /C=US/L=Somewhere/CN=John Doe/emailAddress=john@example.com In addition the old
+# behaviour was to remap any character other than alphanumeric, underscore ('_'),
+# dash ('-'), dot ('.'), and slash ('/') to underscore ('_'). The X.509 Subject
+# string as returned by the tls_id environmental variable, could additionally
+# contain colon (':') or equal ('='). When using the --compat-names option, this
+# old formatting and remapping will be re-enabled again. This is purely implemented
+# for compatibility reasons when using older plug-ins or scripts which does not
+# handle the new formatting or UTF-8 characters.
+#
+# See https://phabricator.vyos.net/T1512
+compat-names
+
+{% if options -%}
+#
+# Custom options added by user (not validated)
+#
+
+{% for option in options -%}
+{{ option }}
+{% endfor -%}
+{%- endif %}
diff --git a/data/templates/pppoe/ip-down.script.tmpl b/data/templates/pppoe/ip-down.script.tmpl
new file mode 100644
index 000000000..c2d0cd09a
--- /dev/null
+++ b/data/templates/pppoe/ip-down.script.tmpl
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+if [ "$6" != "{{ ifname }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+{% if connect_on_demand is not defined %}
+# See https://phabricator.vyos.net/T2248. Determine if we are enslaved to a
+# VRF, this is needed to properly insert the default route.
+VRF_NAME=""
+if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # Populate variable to run in VR context
+ VRF_NAME="vrf ${VRF_NAME}"
+fi
+
+# Always delete default route when interface goes down
+vtysh -c "conf t" ${VRF_NAME} -c "no ip route 0.0.0.0/0 {{ ifname }} ${VRF_NAME}"
+{% if ipv6_enable %}
+vtysh -c "conf t" ${VRF_NAME} -c "no ipv6 route ::/0 {{ ifname }} ${VRF_NAME}"
+{% endif %}
+{% endif %}
+
+{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
+# Stop wide dhcpv6 client
+systemctl stop dhcp6c@{{ ifname }}.service
+{% endif %}
diff --git a/data/templates/pppoe/ip-pre-up.script.tmpl b/data/templates/pppoe/ip-pre-up.script.tmpl
new file mode 100644
index 000000000..cf85ed067
--- /dev/null
+++ b/data/templates/pppoe/ip-pre-up.script.tmpl
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+if [ "$6" != "{{ ifname }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias
+
+{% if vrf -%}
+logger -t pppd[$DIALER_PID] "configuring dialer interface $6 for VRF {{ vrf }}"
+ip link set dev {{ ifname }} master {{ vrf }}
+{% endif %}
diff --git a/data/templates/pppoe/ip-up.script.tmpl b/data/templates/pppoe/ip-up.script.tmpl
new file mode 100644
index 000000000..568e21c4e
--- /dev/null
+++ b/data/templates/pppoe/ip-up.script.tmpl
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+if [ "$6" != "{{ ifname }}" ]; then
+ exit
+fi
+
+{% if connect_on_demand is not defined %}
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+{% if default_route != 'none' -%}
+# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved
+# to a VRF, this is needed to properly insert the default route.
+
+SED_OPT="^ip route"
+VRF_NAME=""
+if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # generate new SED command
+ SED_OPT="vrf ${VRF}"
+ # generate vtysh option
+ VRF_NAME="vrf ${VRF}"
+fi
+
+{% if default_route == 'auto' -%}
+# Only insert a new default route if there is no default route configured
+routes=$(vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | wc -l)
+if [ "$routes" -ne 0 ]; then
+ exit 1
+fi
+
+{% elif default_route == 'force' -%}
+# Retrieve current static default routes and remove it from the routing table
+vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep 0.0.0.0/0 | while read route ; do
+ vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}"
+done
+{% endif %}
+
+# Add default route to default or VRF routing table
+vtysh -c "conf t" ${VTY_OPT} -c "ip route 0.0.0.0/0 {{ ifname }} ${VRF_NAME}"
+logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} ${VRF_NAME}"
+{% endif %}
+{% endif %}
diff --git a/data/templates/pppoe/ipv6-up.script.tmpl b/data/templates/pppoe/ipv6-up.script.tmpl
new file mode 100644
index 000000000..d0a62478c
--- /dev/null
+++ b/data/templates/pppoe/ipv6-up.script.tmpl
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+# As PPPoE is an "on demand" interface we need to re-configure it when it
+# becomes up
+
+if [ "$6" != "{{ ifname }}" ]; then
+ exit
+fi
+
+{% if ipv6 is defined and ipv6.address is defined and ipv6.address.autoconf is defined -%}
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} via {{ source_interface }}"
+
+# Configure interface-specific Host/Router behaviour.
+# Note: It is recommended to have the same setting on all interfaces; mixed
+# router/host scenarios are rather uncommon. Possible values are:
+#
+# 0 Forwarding disabled
+# 1 Forwarding enabled
+#
+echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/forwarding
+
+# Accept Router Advertisements; autoconfigure using them.
+#
+# It also determines whether or not to transmit Router
+# Solicitations. If and only if the functional setting is to
+# accept Router Advertisements, Router Solicitations will be
+# transmitted. Possible values are:
+#
+# 0 Do not accept Router Advertisements.
+# 1 Accept Router Advertisements if forwarding is disabled.
+# 2 Overrule forwarding behaviour. Accept Router Advertisements
+# even if forwarding is enabled.
+#
+echo 2 > /proc/sys/net/ipv6/conf/{{ ifname }}/accept_ra
+
+# Autoconfigure addresses using Prefix Information in Router Advertisements.
+echo 1 > /proc/sys/net/ipv6/conf/{{ ifname }}/autoconf
+{% endif %}
+
+{% if dhcpv6_options is defined and dhcpv6_options.pd is defined %}
+# Start wide dhcpv6 client
+systemctl start dhcp6c@{{ ifname }}.service
+{% endif %}
+
+{% if default_route != 'none' -%}
+# See https://phabricator.vyos.net/T2248 & T2220. Determine if we are enslaved
+# to a VRF, this is needed to properly insert the default route.
+
+SED_OPT="^ipv6 route"
+VRF_NAME=""
+if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF=${VRF#"upper_"}
+ # generate new SED command
+ SED_OPT="vrf ${VRF}"
+ # generate vtysh option
+ VRF_NAME="vrf ${VRF}"
+fi
+
+{% if default_route == 'auto' -%}
+# Only insert a new default route if there is no default route configured
+routes=$(vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep ::/0 | wc -l)
+if [ "$routes" -ne 0 ]; then
+ exit 1
+fi
+
+{% elif default_route == 'force' -%}
+# Retrieve current static default routes and remove it from the routing table
+vtysh -c "show running-config" | sed -n "/${SED_OPT}/,/!/p" | grep ::/0 | while read route ; do
+ vtysh -c "conf t" ${VTY_OPT} -c "no ${route} ${VRF_NAME}"
+done
+{% endif %}
+
+# Add default route to default or VRF routing table
+vtysh -c "conf t" ${VTY_OPT} -c "ipv6 route ::/0 {{ ifname }} ${VRF_NAME}"
+logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} ${VRF_NAME}"
+{% endif %}
+
diff --git a/data/templates/pppoe/peer.tmpl b/data/templates/pppoe/peer.tmpl
new file mode 100644
index 000000000..e909843a5
--- /dev/null
+++ b/data/templates/pppoe/peer.tmpl
@@ -0,0 +1,76 @@
+### Autogenerated by interfaces-pppoe.py ###
+
+{% if description %}
+# {{ description }}
+{% endif %}
+
+# Require peer to provide the local IP address if it is not
+# specified explicitly in the config file.
+noipdefault
+
+# Don't show the password in logfiles:
+hide-password
+
+# Standard Link Control Protocol (LCP) parameters:
+lcp-echo-interval 20
+lcp-echo-failure 3
+
+# RFC 2516, paragraph 7 mandates that the following options MUST NOT be
+# requested and MUST be rejected if requested by the peer:
+# Address-and-Control-Field-Compression (ACFC)
+noaccomp
+
+# Asynchronous-Control-Character-Map (ACCM)
+default-asyncmap
+
+# Override any connect script that may have been set in /etc/ppp/options.
+connect /bin/true
+
+# Don't try to authenticate the remote node
+noauth
+
+# Don't try to proxy ARP for the remote endpoint. User can set proxy
+# arp entries up manually if they wish. More importantly, having
+# the "proxyarp" parameter set disables the "defaultroute" option.
+noproxyarp
+
+# Unlimited connection attempts
+maxfail 0
+
+plugin rp-pppoe.so
+{{ source_interface }}
+persist
+ifname {{ ifname }}
+ipparam {{ ifname }}
+debug
+mtu {{ mtu }}
+mru {{ mtu }}
+
+{% if authentication is defined %}
+{{ "user " + authentication.user if authentication.user is defined }}
+{{ "password " + authentication.password if authentication.password is defined }}
+{% endif %}
+
+{{ "usepeerdns" if no_peer_dns is not defined }}
+
+{% if ipv6 is defined and ipv6.enable is defined -%}
++ipv6
+ipv6cp-use-ipaddr
+{% endif %}
+
+{% if service_name is defined -%}
+rp_pppoe_service "{{ service_name }}"
+{% endif %}
+
+{% if connect_on_demand is defined %}
+demand
+# See T2249. PPP default route options should only be set when in on-demand
+# mode. As soon as we are not in on-demand mode the default-route handling is
+# passed to the ip-up.d/ip-down.s scripts which is required for VRF support.
+{% if 'auto' in default_route -%}
+defaultroute
+{% elif 'force' in default_route -%}
+defaultroute
+replacedefaultroute
+{% endif %}
+{% endif %}
diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.tmpl
new file mode 100644
index 000000000..cebfc54b5
--- /dev/null
+++ b/data/templates/router-advert/radvd.conf.tmpl
@@ -0,0 +1,47 @@
+### Autogenerated by service_router-advert.py ###
+
+{% if interface is defined and interface is not none %}
+{% for iface in interface %}
+interface {{ iface }} {
+ IgnoreIfMissing on;
+{% if interface[iface].default_preference is defined and interface[iface].default_preference is not none %}
+ AdvDefaultPreference {{ interface[iface].default_preference }};
+{% endif %}
+{% if interface[iface].managed_flag is defined and interface[iface].managed_flag is not none %}
+ AdvManagedFlag {{ 'on' if interface[iface].managed_flag is defined else 'off' }};
+{% endif %}
+{% if interface[iface].interval.max is defined and interface[iface].interval.max is not none %}
+ MaxRtrAdvInterval {{ interface[iface].interval.max }};
+{% endif %}
+{% if interface[iface].interval.min is defined and interface[iface].interval.min is not none %}
+ MinRtrAdvInterval {{ interface[iface].interval.min }};
+{% endif %}
+{% if interface[iface].reachable_time is defined and interface[iface].reachable_time is not none %}
+ AdvReachableTime {{ interface[iface].reachable_time }};
+{% endif %}
+ AdvIntervalOpt {{ 'off' if interface[iface].no_send_advert is defined else 'on' }};
+ AdvSendAdvert {{ 'off' if interface[iface].no_send_advert is defined else 'on' }};
+{% if interface[iface].default_lifetime is defined %}
+ AdvDefaultLifetime {{ interface[iface].default_lifetime }};
+{% endif %}
+{% if interface[iface].link_mtu is defined %}
+ AdvLinkMTU {{ interface[iface].link_mtu }};
+{% endif %}
+ AdvOtherConfigFlag {{ 'on' if interface[iface].other_config_flag is defined else 'off' }};
+ AdvRetransTimer {{ interface[iface].retrans_timer }};
+ AdvCurHopLimit {{ interface[iface].hop_limit }};
+{% for prefix in interface[iface].prefix %}
+ prefix {{ prefix }} {
+ AdvAutonomous {{ 'off' if interface[iface].prefix[prefix].no_autonomous_flag is defined else 'on' }};
+ AdvValidLifetime {{ interface[iface].prefix[prefix].valid_lifetime }};
+ AdvOnLink {{ 'off' if interface[iface].prefix[prefix].no_on_link_flag is defined else 'on' }};
+ AdvPreferredLifetime {{ interface[iface].prefix[prefix].preferred_lifetime }};
+ };
+{% endfor %}
+{% if interface[iface].name_server is defined %}
+ RDNSS {{ interface[iface].name_server | join(" ") }} {
+ };
+{% endif %}
+};
+{% endfor -%}
+{% endif %}
diff --git a/data/templates/rsyslog/rsyslog.conf b/data/templates/rsyslog/rsyslog.conf
new file mode 100644
index 000000000..ab60fc0f0
--- /dev/null
+++ b/data/templates/rsyslog/rsyslog.conf
@@ -0,0 +1,59 @@
+# /etc/rsyslog.conf Configuration file for rsyslog.
+#
+
+#################
+#### MODULES ####
+#################
+
+$ModLoad imuxsock # provides support for local system logging
+$ModLoad imklog # provides kernel logging support (previously done by rklogd)
+#$ModLoad immark # provides --MARK-- message capability
+
+$OmitLocalLogging off
+$SystemLogSocketName /run/systemd/journal/syslog
+
+$KLogPath /proc/kmsg
+
+# provides UDP syslog reception
+#$ModLoad imudp
+#$UDPServerRun 514
+
+# provides TCP syslog reception
+#$ModLoad imtcp
+#$InputTCPServerRun 514
+
+###########################
+#### GLOBAL DIRECTIVES ####
+###########################
+
+#
+# Use traditional timestamp format.
+# To enable high precision timestamps, comment out the following line.
+#
+$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
+
+# Filter duplicated messages
+$RepeatedMsgReduction on
+
+#
+# Set the default permissions for all log files.
+#
+$FileOwner root
+$FileGroup adm
+$FileCreateMode 0640
+$DirCreateMode 0755
+$Umask 0022
+
+
+#
+# Include all config files in /etc/rsyslog.d/
+#
+$IncludeConfig /etc/rsyslog.d/*.conf
+
+###############
+#### RULES ####
+###############
+# Emergencies are sent to everybody logged in.
+
+*.emerg :omusrmsg:*
+
diff --git a/data/templates/salt-minion/minion.tmpl b/data/templates/salt-minion/minion.tmpl
new file mode 100644
index 000000000..9369573a4
--- /dev/null
+++ b/data/templates/salt-minion/minion.tmpl
@@ -0,0 +1,59 @@
+### Autogenerated by salt-minion.py ###
+
+##### Primary configuration settings #####
+##########################################
+
+# The hash_type is the hash to use when discovering the hash of a file on
+# the master server. The default is sha256, but md5, sha1, sha224, sha384 and
+# sha512 are also supported.
+#
+# WARNING: While md5 and sha1 are also supported, do not use them due to the
+# high chance of possible collisions and thus security breach.
+#
+# Prior to changing this value, the master should be stopped and all Salt
+# caches should be cleared.
+hash_type: {{ hash }}
+
+##### Logging settings #####
+##########################################
+# The location of the minion log file
+# The minion log can be sent to a regular file, local path name, or network
+# location. Remote logging works best when configured to use rsyslogd(8) (e.g.:
+# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI
+# format is: <file|udp|tcp>://<host|socketpath>:<port-if-required>/<log-facility>
+log_file: file:///dev/log
+
+# The level of messages to send to the console.
+# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'.
+#
+# The following log levels are considered INSECURE and may log sensitive data:
+# ['garbage', 'trace', 'debug']
+#
+# Default: 'warning'
+log_level: {{ log_level }}
+
+# Set the location of the salt master server, if the master server cannot be
+# resolved, then the minion will fail to start.
+master:
+{% for host in master -%}
+- {{ host }}
+{% endfor %}
+
+# The user to run salt
+user: {{ user }}
+
+# The directory to store the pki information in
+pki_dir: /config/salt/pki/minion
+
+# Explicitly declare the id for this minion to use, if left commented the id
+# will be the hostname as returned by the python call: socket.getfqdn()
+# Since salt uses detached ids it is possible to run multiple minions on the
+# same machine but with different ids, this can be useful for salt compute
+# clusters.
+id: {{ salt_id }}
+
+
+# The number of minutes between mine updates.
+mine_interval: {{ interval }}
+
+verify_master_pubkey_sign: {{ verify_master_pubkey_sign }}
diff --git a/data/templates/snmp/etc.snmp.conf.tmpl b/data/templates/snmp/etc.snmp.conf.tmpl
new file mode 100644
index 000000000..6e4c6f063
--- /dev/null
+++ b/data/templates/snmp/etc.snmp.conf.tmpl
@@ -0,0 +1,4 @@
+### Autogenerated by snmp.py ###
+{% if trap_source %}
+clientaddr {{ trap_source }}
+{% endif %}
diff --git a/data/templates/snmp/etc.snmpd.conf.tmpl b/data/templates/snmp/etc.snmpd.conf.tmpl
new file mode 100644
index 000000000..278506350
--- /dev/null
+++ b/data/templates/snmp/etc.snmpd.conf.tmpl
@@ -0,0 +1,115 @@
+### Autogenerated by snmp.py ###
+
+# non configurable defaults
+sysObjectID 1.3.6.1.4.1.44641
+sysServices 14
+master agentx
+agentXPerms 0777 0777
+pass .1.3.6.1.2.1.31.1.1.1.18 /opt/vyatta/sbin/if-mib-alias
+smuxpeer .1.3.6.1.2.1.83
+smuxpeer .1.3.6.1.2.1.157
+smuxsocket localhost
+
+# linkUp/Down configure the Event MIB tables to monitor
+# the ifTable for network interfaces being taken up or down
+# for making internal queries to retrieve any necessary information
+iquerySecName {{ vyos_user }}
+
+# Modified from the default linkUpDownNotification
+# to include more OIDs and poll more frequently
+notificationEvent linkUpTrap linkUp ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+notificationEvent linkDownTrap linkDown ifIndex ifDescr ifType ifAdminStatus ifOperStatus
+monitor -r 10 -e linkUpTrap "Generate linkUp" ifOperStatus != 2
+monitor -r 10 -e linkDownTrap "Generate linkDown" ifOperStatus == 2
+
+########################
+# configurable section #
+########################
+
+# Default system description is VyOS version
+sysDescr VyOS {{ version }}
+
+{% if description %}
+# Description
+SysDescr {{ description }}
+{% endif %}
+
+# Listen
+agentaddress unix:/run/snmpd.socket{% if listen_on %}{% for li in listen_on %},{{ li }}{% endfor %}{% else %},udp:161{% if ipv6_enabled %},udp6:161{% endif %}{% endif %}
+
+# SNMP communities
+{% for c in communities %}
+{% if c.network_v4 %}
+{% for network in c.network_v4 %}
+{{ c.authorization }}community {{ c.name }} {{ network }}
+{% endfor %}
+{% elif not c.has_source %}
+{{ c.authorization }}community {{ c.name }}
+{% endif %}
+{% if c.network_v6 %}
+{% for network in c.network_v6 %}
+{{ c.authorization }}community6 {{ c.name }} {{ network }}
+{% endfor %}
+{% elif not c.has_source %}
+{{ c.authorization }}community6 {{ c.name }}
+{% endif %}
+{% endfor %}
+
+{% if contact %}
+# system contact information
+SysContact {{ contact }}
+{% endif %}
+
+{% if location %}
+# system location information
+SysLocation {{ location }}
+{% endif %}
+
+{% if smux_peers %}
+# additional smux peers
+{% for sp in smux_peers %}
+smuxpeer {{ sp }}
+{% endfor %}
+{% endif %}
+
+{% if trap_targets %}
+# if there is a problem - tell someone!
+{% for trap in trap_targets %}
+trap2sink {{ trap.target }}{{ ":" + trap.port if trap.port is defined }} {{ trap.community }}
+{% endfor %}
+{% endif %}
+
+{% if v3_enabled %}
+#
+# SNMPv3 stuff goes here
+#
+# views
+{% for view in v3_views %}
+{% for oid in view.oids %}
+view {{ view.name }} included .{{ oid.oid }}
+{% endfor %}
+{% endfor %}
+
+# access
+# context sec.model sec.level match read write notif
+{% for group in v3_groups %}
+access {{ group.name }} "" usm {{ group.seclevel }} exact {{ group.view }} {% if group.mode == 'ro' %}none{% else %}{{ group.view }}{% endif %} none
+{% endfor %}
+
+# trap-target
+{% for t in v3_traps %}
+trapsess -v 3 {{ '-Ci' if t.type == 'inform' }} -e {{ v3_engineid }} -u {{ t.secName }} -l {{ t.secLevel }} -a {{ t.authProtocol }} {% if t.authPassword %}-A {{ t.authPassword }}{% elif t.authMasterKey %}-3m {{ t.authMasterKey }}{% endif %} -x {{ t.privProtocol }} {% if t.privPassword %}-X {{ t.privPassword }}{% elif t.privMasterKey %}-3M {{ t.privMasterKey }}{% endif %} {{ t.ipProto }}:{{ t.ipAddr }}:{{ t.ipPort }}
+{% endfor %}
+
+# group
+{% for u in v3_users %}
+group {{ u.group }} usm {{ u.name }}
+{% endfor %}
+{% endif %}
+
+{% if script_ext %}
+# extension scripts
+{% for ext in script_ext|sort(attribute='name') %}
+extend {{ ext.name }} {{ ext.script }}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/snmp/override.conf.tmpl b/data/templates/snmp/override.conf.tmpl
new file mode 100644
index 000000000..e6302a9e1
--- /dev/null
+++ b/data/templates/snmp/override.conf.tmpl
@@ -0,0 +1,13 @@
+{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+
+[Service]
+Environment=
+Environment="MIBSDIR=/usr/share/snmp/mibs:/usr/share/snmp/mibs/iana:/usr/share/snmp/mibs/ietf:/usr/share/mibs/site:/usr/share/snmp/mibs:/usr/share/mibs/iana:/usr/share/mibs/ietf:/usr/share/mibs/netsnmp"
+ExecStart=
+ExecStart={{vrf_command}}/usr/sbin/snmpd -LS0-5d -Lf /dev/null -u Debian-snmp -g Debian-snmp -I -ipCidrRouteTable,inetCidrRouteTable -f -p /run/snmpd.pid
+Restart=on-failure
+RestartSec=10
+
diff --git a/data/templates/snmp/usr.snmpd.conf.tmpl b/data/templates/snmp/usr.snmpd.conf.tmpl
new file mode 100644
index 000000000..9c0337fa8
--- /dev/null
+++ b/data/templates/snmp/usr.snmpd.conf.tmpl
@@ -0,0 +1,6 @@
+### Autogenerated by snmp.py ###
+{%- for u in v3_users %}
+{{ u.mode }}user {{ u.name }}
+{%- endfor %}
+
+rwuser {{ vyos_user }}
diff --git a/data/templates/snmp/var.snmpd.conf.tmpl b/data/templates/snmp/var.snmpd.conf.tmpl
new file mode 100644
index 000000000..6cbc687ef
--- /dev/null
+++ b/data/templates/snmp/var.snmpd.conf.tmpl
@@ -0,0 +1,14 @@
+### Autogenerated by snmp.py ###
+# user
+{%- for u in v3_users %}
+{%- if u.authOID == 'none' %}
+createUser {{ u.name }}
+{%- else %}
+usmUser 1 3 0x{{ v3_engineid }} "{{ u.name }}" "{{ u.name }}" NULL {{ u.authOID }} 0x{{ u.authMasterKey }} {{ u.privOID }} 0x{{ u.privMasterKey }} 0x
+{%- endif %}
+{%- endfor %}
+
+createUser {{ vyos_user }} MD5 "{{ vyos_user_pass }}" DES
+{%- if v3_engineid %}
+oldEngineID 0x{{ v3_engineid }}
+{%- endif %}
diff --git a/data/templates/ssh/override.conf.tmpl b/data/templates/ssh/override.conf.tmpl
new file mode 100644
index 000000000..843aa927b
--- /dev/null
+++ b/data/templates/ssh/override.conf.tmpl
@@ -0,0 +1,11 @@
+{% set vrf_command = '/sbin/ip vrf exec ' + vrf + ' ' if vrf is defined else '' %}
+[Unit]
+StartLimitIntervalSec=0
+After=vyos-router.service
+ConditionPathExists={{config_file}}
+
+[Service]
+ExecStart=
+ExecStart={{vrf_command}}/usr/sbin/sshd -f {{config_file}} -D $SSHD_OPTS
+RestartSec=10
+
diff --git a/data/templates/ssh/sshd_config.tmpl b/data/templates/ssh/sshd_config.tmpl
new file mode 100644
index 000000000..4fde24255
--- /dev/null
+++ b/data/templates/ssh/sshd_config.tmpl
@@ -0,0 +1,114 @@
+### Autogenerated by ssh.py ###
+
+# https://linux.die.net/man/5/sshd_config
+
+#
+# Non-configurable defaults
+#
+Protocol 2
+HostKey /etc/ssh/ssh_host_rsa_key
+HostKey /etc/ssh/ssh_host_dsa_key
+HostKey /etc/ssh/ssh_host_ecdsa_key
+HostKey /etc/ssh/ssh_host_ed25519_key
+SyslogFacility AUTH
+LoginGraceTime 120
+StrictModes yes
+PubkeyAuthentication yes
+IgnoreRhosts yes
+HostbasedAuthentication no
+PermitEmptyPasswords no
+ChallengeResponseAuthentication no
+X11Forwarding yes
+X11DisplayOffset 10
+PrintMotd no
+PrintLastLog yes
+TCPKeepAlive yes
+Banner /etc/issue.net
+Subsystem sftp /usr/lib/openssh/sftp-server
+UsePAM yes
+PermitRootLogin no
+
+#
+# User configurable section
+#
+
+# Look up remote host name and check that the resolved host name for the remote IP
+# address maps back to the very same IP address.
+UseDNS {{ "no" if disable_host_validation is defined else "yes" }}
+
+# Specifies the port number that sshd(8) listens on
+{% if port is string %}
+Port {{ port }}
+{% else %}
+{% for value in port %}
+Port {{ value }}
+{% endfor %}
+{% endif %}
+
+# Gives the verbosity level that is used when logging messages from sshd
+LogLevel {{ loglevel | upper }}
+
+# Specifies whether password authentication is allowed
+PasswordAuthentication {{ "no" if disable_password_authentication is defined else "yes" }}
+
+{% if listen_address %}
+# Specifies the local addresses sshd should listen on
+{% if listen_address is string %}
+ListenAddress {{ listen_address }}
+{% else %}
+{% for address in listen_address %}
+ListenAddress {{ address }}
+{% endfor %}
+{% endif %}
+{% endif %}
+
+{% if ciphers %}
+# Specifies the ciphers allowed for protocol version 2
+{% set value = ciphers if ciphers is string else ciphers | join(',') %}
+Ciphers {{ value }}
+{% endif %}
+
+{% if mac %}
+# Specifies the available MAC (message authentication code) algorithms
+{% set value = mac if mac is string else mac | join(',') %}
+MACs {{ value }}
+{% endif %}
+
+{% if key_exchange %}
+# Specifies the available Key Exchange algorithms
+{% set value = key_exchange if key_exchange is string else key_exchange | join(',') %}
+KexAlgorithms {{ value }}
+{% endif %}
+
+{% if access_control is defined %}
+{% if access_control.allow is defined %}
+{% if access_control.allow.user is defined %}
+# If specified, login is allowed only for user names that match
+{% set value = access_control.allow.user if access_control.allow.user is string else access_control.allow.user | join(' ') %}
+AllowUsers {{ value }}
+{% endif %}
+{% if access_control.allow.group is defined %}
+# If specified, login is allowed only for users whose primary group or supplementary group list matches
+{% set value = access_control.allow.group if access_control.allow.group is string else access_control.allow.group | join(' ') %}
+AllowGroups {{ value }}
+{% endif %}
+{% endif %}
+{% if access_control.deny is defined %}
+{% if access_control.deny.user is defined %}
+# Login is disallowed for user names that match
+{% set value = access_control.deny.user if access_control.deny.user is string else access_control.deny.user | join(' ') %}
+DenyUsers {{ value }}
+{% endif %}
+{% if access_control.deny.group is defined %}
+# Login is disallowed for users whose primary group or supplementary group list matches
+{% set value = access_control.deny.group if access_control.deny.group is string else access_control.deny.group | join(' ') %}
+DenyGroups {{ value }}
+{% endif %}
+{% endif %}
+{% endif %}
+
+{% if client_keepalive_interval %}
+# Sets a timeout interval in seconds after which if no data has been received from the client,
+# sshd(8) will send a message through the encrypted channel to request a response from the client
+ClientAliveInterval {{ client_keepalive_interval }}
+{% endif %}
diff --git a/data/templates/syslog/logrotate.tmpl b/data/templates/syslog/logrotate.tmpl
new file mode 100644
index 000000000..f758265e4
--- /dev/null
+++ b/data/templates/syslog/logrotate.tmpl
@@ -0,0 +1,12 @@
+{% for file in files %}
+{{files[file]['log-file']}} {
+ missingok
+ notifempty
+ create
+ rotate {{files[file]['max-files']}}
+ size={{files[file]['max-size']//1024}}k
+ postrotate
+ invoke-rc.d rsyslog rotate > /dev/null
+ endscript
+}
+{% endfor %}
diff --git a/data/templates/syslog/rsyslog.conf.tmpl b/data/templates/syslog/rsyslog.conf.tmpl
new file mode 100644
index 000000000..bc3f7667b
--- /dev/null
+++ b/data/templates/syslog/rsyslog.conf.tmpl
@@ -0,0 +1,44 @@
+## generated by syslog.py ##
+## file based logging
+{% if files['global']['marker'] -%}
+$ModLoad immark
+{% if files['global']['marker-interval'] %}
+$MarkMessagePeriod {{files['global']['marker-interval']}}
+{% endif %}
+{% endif -%}
+{% if files['global']['preserver_fqdn'] -%}
+$PreserveFQDN on
+{% endif -%}
+{% for file in files %}
+$outchannel {{file}},{{files[file]['log-file']}},{{files[file]['max-size']}},{{files[file]['action-on-max-size']}}
+{{files[file]['selectors']}} :omfile:${{file}}
+{% endfor %}
+{% if console %}
+## console logging
+{% for con in console %}
+{{console[con]['selectors']}} /dev/console
+{% endfor %}
+{% endif %}
+{% if hosts %}
+## remote logging
+{% for host in hosts %}
+{% if hosts[host]['proto'] == 'tcp' %}
+{% if hosts[host]['port'] %}
+{{hosts[host]['selectors']}} @@{{host}}:{{hosts[host]['port']}}
+{% else %}
+{{hosts[host]['selectors']}} @@{{host}}
+{% endif %}
+{% else %}
+{% if hosts[host]['port'] %}
+{{hosts[host]['selectors']}} @{{host}}:{{hosts[host]['port']}}
+{% else %}
+{{hosts[host]['selectors']}} @{{host}}
+{% endif %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if user %}
+{% for u in user %}
+{{user[u]['selectors']}} :omusrmsg:{{u}}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/system-login/pam_radius_auth.conf.tmpl b/data/templates/system-login/pam_radius_auth.conf.tmpl
new file mode 100644
index 000000000..ec2d6df95
--- /dev/null
+++ b/data/templates/system-login/pam_radius_auth.conf.tmpl
@@ -0,0 +1,16 @@
+# Automatically generated by system-login.py
+# RADIUS configuration file
+{% if radius_server %}
+# server[:port] shared_secret timeout source_ip
+{% for s in radius_server|sort(attribute='priority') if not s.disabled %}
+{% set addr_port = s.address + ":" + s.port %}
+{{ "%-22s" | format(addr_port) }} {{ "%-25s" | format(s.key) }} {{ "%-10s" | format(s.timeout) }} {{ radius_source_address if radius_source_address }}
+{% endfor %}
+
+priv-lvl 15
+mapped_priv_user radius_priv_user
+
+{% if radius_vrf %}
+vrf-name {{ radius_vrf }}
+{% endif %}
+{% endif %}
diff --git a/data/templates/system/curlrc.tmpl b/data/templates/system/curlrc.tmpl
new file mode 100644
index 000000000..3e5ce801c
--- /dev/null
+++ b/data/templates/system/curlrc.tmpl
@@ -0,0 +1,8 @@
+{% if http_client is defined %}
+{% if http_client.source_interface is defined %}
+--interface "{{ http_client.source_interface }}"
+{% endif %}
+{% if http_client.source_address is defined %}
+--interface "{{ http_client.source_address }}"
+{% endif %}
+{% endif %}
diff --git a/data/templates/system/ssh_config.tmpl b/data/templates/system/ssh_config.tmpl
new file mode 100644
index 000000000..509bd5479
--- /dev/null
+++ b/data/templates/system/ssh_config.tmpl
@@ -0,0 +1,3 @@
+{% if ssh_client is defined and ssh_client.source_address is defined and ssh_client.source_address is not none %}
+BindAddress {{ ssh_client.source_address }}
+{% endif %}
diff --git a/data/templates/tftp-server/default.tmpl b/data/templates/tftp-server/default.tmpl
new file mode 100644
index 000000000..18fee35d1
--- /dev/null
+++ b/data/templates/tftp-server/default.tmpl
@@ -0,0 +1,2 @@
+### Autogenerated by tftp_server.py ###
+DAEMON_ARGS="--listen --user tftp --address {% for a in listen-%}{{ a }}{% endfor %}{% if allow_upload %} --create --umask 000{% endif %} --secure {{ directory }}"
diff --git a/data/templates/vrf/vrf.conf.tmpl b/data/templates/vrf/vrf.conf.tmpl
new file mode 100644
index 000000000..761b0bb6f
--- /dev/null
+++ b/data/templates/vrf/vrf.conf.tmpl
@@ -0,0 +1,8 @@
+### Autogenerated by vrf.py ###
+#
+# Routing table ID to name mapping reference
+
+# id vrf name comment
+{% for vrf in vrf_add -%}
+{{ "%-10s" | format(vrf.table) }} {{ "%-16s" | format(vrf.name) }} # {{ vrf.description }}
+{% endfor -%}
diff --git a/data/templates/vrrp/daemon.tmpl b/data/templates/vrrp/daemon.tmpl
new file mode 100644
index 000000000..c9dbea72d
--- /dev/null
+++ b/data/templates/vrrp/daemon.tmpl
@@ -0,0 +1,5 @@
+# Autogenerated by VyOS
+# Options to pass to keepalived
+
+# DAEMON_ARGS are appended to the keepalived command-line
+DAEMON_ARGS="--snmp"
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/vrrp/keepalived.conf.tmpl
new file mode 100644
index 000000000..08b821f70
--- /dev/null
+++ b/data/templates/vrrp/keepalived.conf.tmpl
@@ -0,0 +1,97 @@
+# Autogenerated by VyOS
+# Do not edit this file, all your changes will be lost
+# on next commit or reboot
+
+global_defs {
+ dynamic_interfaces
+ script_user root
+ notify_fifo /run/keepalived_notify_fifo
+ notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
+}
+
+{% for group in groups -%}
+
+{% if group.health_check_script -%}
+vrrp_script healthcheck_{{ group.name }} {
+ script "{{ group.health_check_script }}"
+ interval {{ group.health_check_interval }}
+ fall {{ group.health_check_count }}
+ rise 1
+
+}
+{% endif %}
+
+vrrp_instance {{ group.name }} {
+ {% if group.description -%}
+ # {{ group.description }}
+ {% endif -%}
+
+ state BACKUP
+ interface {{ group.interface }}
+ virtual_router_id {{ group.vrid }}
+ priority {{ group.priority }}
+ advert_int {{ group.advertise_interval }}
+
+ {% if group.preempt -%}
+ preempt_delay {{ group.preempt_delay }}
+ {% else -%}
+ nopreempt
+ {% endif -%}
+
+ {% if group.peer_address -%}
+ unicast_peer { {{ group.peer_address }} }
+ {% endif -%}
+
+ {% if group.hello_source -%}
+ {%- if group.peer_address -%}
+ unicast_src_ip {{ group.hello_source }}
+ {%- else -%}
+ mcast_src_ip {{ group.hello_source }}
+ {%- endif %}
+ {% endif -%}
+
+ {% if group.use_vmac and group.peer_address -%}
+ use_vmac {{group.interface}}v{{group.vrid}}
+ vmac_xmit_base
+ {% elif group.use_vmac -%}
+ use_vmac {{group.interface}}v{{group.vrid}}
+ {% endif -%}
+
+ {% if group.auth_password -%}
+ authentication {
+ auth_pass "{{ group.auth_password }}"
+ auth_type {{ group.auth_type }}
+ }
+ {% endif -%}
+
+ virtual_ipaddress {
+ {% for addr in group.virtual_addresses -%}
+ {{ addr }}
+ {% endfor -%}
+ }
+
+ {% if group.health_check_script -%}
+ track_script {
+ healthcheck_{{ group.name }}
+ }
+ {% endif -%}
+}
+
+{% endfor -%}
+
+{% for sync_group in sync_groups -%}
+vrrp_sync_group {{ sync_group.name }} {
+ group {
+ {% for member in sync_group.members -%}
+ {{ member }}
+ {% endfor -%}
+ }
+
+ {% if sync_group.conntrack_sync -%}
+ notify_master "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh master {{ sync_group.name }}"
+ notify_backup "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh backup {{ sync_group.name }}"
+ notify_fault "/opt/vyatta/sbin/vyatta-vrrp-conntracksync.sh fault {{ sync_group.name }}"
+ {% endif -%}
+}
+
+{% endfor -%}
diff --git a/data/templates/vyos-hostsd/hosts.tmpl b/data/templates/vyos-hostsd/hosts.tmpl
new file mode 100644
index 000000000..566f9a5dd
--- /dev/null
+++ b/data/templates/vyos-hostsd/hosts.tmpl
@@ -0,0 +1,26 @@
+### Autogenerated by VyOS ###
+### Do not edit, your changes will get overwritten ###
+
+# Local host
+127.0.0.1 localhost
+127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }} {{ host_name }}{% endif %}
+
+# The following lines are desirable for IPv6 capable hosts
+::1 localhost ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+
+{% if hosts -%}
+# From 'system static-host-mapping' and DHCP server
+{%- for tag, taghosts in hosts.items() %}
+# {{ tag }}
+{%- for host, hostprops in taghosts.items() %}
+{%- if hostprops['address'] %}
+{{ hostprops['address'] }} {{ host }}{% for a in hostprops['aliases'] %} {{ a }}{% endfor %}
+{%- endif %}
+{%- endfor %}
+{%- endfor %}
+{%- endif %}
+
diff --git a/data/templates/vyos-hostsd/resolv.conf.tmpl b/data/templates/vyos-hostsd/resolv.conf.tmpl
new file mode 100644
index 000000000..b920b2e5f
--- /dev/null
+++ b/data/templates/vyos-hostsd/resolv.conf.tmpl
@@ -0,0 +1,26 @@
+### Autogenerated by VyOS ###
+### Do not edit, your changes will get overwritten ###
+
+{#- the code below ensures the order of nameservers is determined first by #}
+{# the order of tags, then by the order of nameservers within that tag #}
+
+{%- for tag in name_server_tags_system %}
+{%- if tag in name_servers %}
+# {{ tag }}
+{%- for ns in name_servers[tag] %}
+nameserver {{ ns }}
+{%- endfor %}
+{%- endif %}
+{%- endfor %}
+
+{%- if domain_name %}
+domain {{ domain_name }}
+{%- endif %}
+
+{% for tag in name_server_tags_system %}
+{%- if tag in search_domains %}
+# {{ tag }}
+search {{ search_domains[tag]|join(' ') }}
+{%- endif %}
+{%- endfor %}
+
diff --git a/data/templates/wifi/cfg80211.conf.tmpl b/data/templates/wifi/cfg80211.conf.tmpl
new file mode 100644
index 000000000..91df57aab
--- /dev/null
+++ b/data/templates/wifi/cfg80211.conf.tmpl
@@ -0,0 +1 @@
+{{ 'options cfg80211 ieee80211_regdom=' + regdom if regdom is defined }}
diff --git a/data/templates/wifi/crda.tmpl b/data/templates/wifi/crda.tmpl
new file mode 100644
index 000000000..6cd125e37
--- /dev/null
+++ b/data/templates/wifi/crda.tmpl
@@ -0,0 +1 @@
+{{ 'REGDOMAIN=' + regdom if regdom is defined }}
diff --git a/data/templates/wifi/hostapd.conf.tmpl b/data/templates/wifi/hostapd.conf.tmpl
new file mode 100644
index 000000000..765668c57
--- /dev/null
+++ b/data/templates/wifi/hostapd.conf.tmpl
@@ -0,0 +1,687 @@
+### Autogenerated by interfaces-wireless.py ###
+{% if description %}
+# Description: {{ description }}
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+device_name={{ description | truncate(32, True) }}
+{% endif %}
+
+# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
+# management frames with the Host AP driver); wlan0 with many nl80211 drivers
+# Note: This attribute can be overridden by the values supplied with the '-i'
+# command line parameter.
+interface={{ ifname }}
+
+# Driver interface type (hostap/wired/none/nl80211/bsd);
+# default: hostap). nl80211 is used with all Linux mac80211 drivers.
+# Use driver=none if building hostapd as a standalone RADIUS server that does
+# not control any wireless/wired driver.
+driver=nl80211
+
+# Levels (minimum value for logged events):
+# 0 = verbose debugging
+# 1 = debugging
+# 2 = informational messages
+# 3 = notification
+# 4 = warning
+logger_syslog=-1
+logger_syslog_level=0
+logger_stdout=-1
+logger_stdout_level=0
+
+{% if country_code %}
+# Country code (ISO/IEC 3166-1). Used to set regulatory domain.
+# Set as needed to indicate country in which device is operating.
+# This can limit available channels and transmit power.
+country_code={{ country_code }}
+
+# Enable IEEE 802.11d. This advertises the country_code and the set of allowed
+# channels and transmit power levels based on the regulatory limits. The
+# country_code setting must be configured with the correct country for
+# IEEE 802.11d functions.
+ieee80211d=1
+{% endif %}
+
+{% if ssid %}
+# SSID to be used in IEEE 802.11 management frames
+ssid={{ ssid }}
+{% endif %}
+
+{% if channel %}
+# Channel number (IEEE 802.11)
+# (default: 0, i.e., not set)
+# Please note that some drivers do not use this value from hostapd and the
+# channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
+channel={{ channel }}
+{% endif %}
+
+{% if mode %}
+# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
+# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
+# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
+# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode=a. When using ACS (see channel parameter), a
+# special value "any" can be used to indicate that any support band can be used.
+# This special case is currently supported only with drivers with which
+# offloaded ACS is used.
+{% if 'n' in mode %}
+hw_mode=g
+{% elif 'ac' in mode %}
+hw_mode=a
+ieee80211h=1
+ieee80211ac=1
+{% else %}
+hw_mode={{ mode }}
+{% endif %}
+{% endif %}
+
+# ieee80211w: Whether management frame protection (MFP) is enabled
+# 0 = disabled (default)
+# 1 = optional
+# 2 = required
+{% if 'disabled' in mgmt_frame_protection %}
+ieee80211w=0
+{% elif 'optional' in mgmt_frame_protection %}
+ieee80211w=1
+{% elif 'required' in mgmt_frame_protection %}
+ieee80211w=2
+{% endif %}
+
+{% if capabilities is defined and capabilities.ht is defined %}
+# ht_capab: HT capabilities (list of flags)
+# LDPC coding capability: [LDPC] = supported
+# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
+# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
+# with secondary channel above the primary channel
+# (20 MHz only if neither is set)
+# Note: There are limits on which channels can be used with HT40- and
+# HT40+. Following table shows the channels that may be available for
+# HT40- and HT40+ use per IEEE 802.11n Annex J:
+# freq HT40- HT40+
+# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan)
+# 5 GHz 40,48,56,64 36,44,52,60
+# (depending on the location, not all of these channels may be available
+# for use)
+# Please note that 40 MHz channels may switch their primary and secondary
+# channels if needed or creation of 40 MHz channel maybe rejected based
+# on overlapping BSSes. These changes are done automatically when hostapd
+# is setting up the 40 MHz channel.
+# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
+# (SMPS disabled if neither is set)
+# HT-greenfield: [GF] (disabled if not set)
+# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
+# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
+# Tx STBC: [TX-STBC] (disabled if not set)
+# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial
+# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC
+# disabled if none of these set
+# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set)
+# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
+# set)
+# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
+# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
+# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
+{% set output = '' %}
+{% set output = output + '[40-INTOLERANT]' if capabilities.ht.fourtymhz_incapable is defined else '' %}
+{% set output = output + '[DELAYED-BA]' if capabilities.ht.delayed_block_ack is defined else '' %}
+{% set output = output + '[DSSS_CCK-40]' if capabilities.ht.dsss_cck_40 is defined else '' %}
+{% set output = output + '[GF]' if capabilities.ht.greenfield is defined else '' %}
+{% set output = output + '[LDPC]' if capabilities.ht.ldpc is defined else '' %}
+{% set output = output + '[LSIG-TXOP-PROT]' if capabilities.ht.lsig_protection is defined else '' %}
+{% set output = output + '[TX-STBC]' if capabilities.ht.stbc.tx is defined else '' %}
+{% set output = output + '[RX-STBC-' + capabilities.ht.stbc.rx | upper + ']' if capabilities.ht.stbc.tx is defined else '' %}
+{% set output = output + '[MAX-AMSDU-' + capabilities.ht.max_amsdu + ']' if capabilities.ht.max_amsdu is defined else '' %}
+{% set output = output + '[SMPS-' + capabilities.ht.smps | upper + ']' if capabilities.ht.smps is defined else '' %}
+
+{% if capabilities.ht.channel_set_width is defined %}
+{% for csw in capabilities.ht.channel_set_width %}
+{% set output = output + '[' + csw | upper + ']' %}
+{% endfor %}
+{% endif %}
+
+{% if capabilities.ht.short_gi is defined %}
+{% for short_gi in capabilities.ht.short_gi %}
+{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %}
+{% endfor %}
+{% endif %}
+
+ht_capab={{ output }}
+
+{% if capabilities.ht.auto_powersave is defined %}
+# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD]
+# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver)
+uapsd_advertisement_enabled=1
+{% endif %}
+
+{% endif %}
+
+# Required for full HT and VHT functionality
+wme_enabled=1
+
+
+{% if capabilities is defined and capabilities.require_ht is defined %}
+# Require stations to support HT PHY (reject association if they do not)
+require_ht=1
+{% endif %}
+
+{% if capabilities is defined and capabilities.vht is defined %}
+# vht_capab: VHT capabilities (list of flags)
+#
+# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
+# Indicates maximum MPDU length
+# 0 = 3895 octets (default)
+# 1 = 7991 octets
+# 2 = 11454 octets
+# 3 = reserved
+#
+# supported_chan_width: [VHT160] [VHT160-80PLUS80]
+# Indicates supported Channel widths
+# 0 = 160 MHz & 80+80 channel widths are not supported (default)
+# 1 = 160 MHz channel width is supported
+# 2 = 160 MHz & 80+80 channel widths are supported
+# 3 = reserved
+#
+# Rx LDPC coding capability: [RXLDPC]
+# Indicates support for receiving LDPC coded pkts
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 80 MHz: [SHORT-GI-80]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 80Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Short GI for 160 MHz: [SHORT-GI-160]
+# Indicates short GI support for reception of packets transmitted with TXVECTOR
+# params format equal to VHT and CBW = 160Mhz
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Tx STBC: [TX-STBC-2BY1]
+# Indicates support for the transmission of at least 2x1 STBC
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234]
+# Indicates support for the reception of PPDUs using STBC
+# 0 = Not supported (default)
+# 1 = support of one spatial stream
+# 2 = support of one and two spatial streams
+# 3 = support of one, two and three spatial streams
+# 4 = support of one, two, three and four spatial streams
+# 5,6,7 = reserved
+#
+# SU Beamformer Capable: [SU-BEAMFORMER]
+# Indicates support for operation as a single user beamformer
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# SU Beamformee Capable: [SU-BEAMFORMEE]
+# Indicates support for operation as a single user beamformee
+# 0 = Not supported (default)
+# 1 = Supported
+#
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
+# Beamformee's capability indicating the maximum number of beamformer
+# antennas the beamformee can support when sending compressed beamforming
+# feedback
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
+# Beamformer's capability indicating the maximum value of the NUM_STS parameter
+# in the TXVECTOR of a VHT NDP
+# If SU beamformer capable, set to maximum value minus 1
+# else reserved (default)
+#
+# MU Beamformer Capable: [MU-BEAMFORMER]
+# Indicates support for operation as an MU beamformer
+# 0 = Not supported or sent by Non-AP STA (default)
+# 1 = Supported
+#
+# VHT TXOP PS: [VHT-TXOP-PS]
+# Indicates whether or not the AP supports VHT TXOP Power Save Mode
+# or whether or not the STA is in VHT TXOP Power Save mode
+# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
+# mode
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
+# mode
+#
+# +HTC-VHT Capable: [HTC-VHT]
+# Indicates whether or not the STA supports receiving a VHT variant HT Control
+# field.
+# 0 = Not supported (default)
+# 1 = supported
+#
+# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7]
+# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv
+# This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+#
+# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3]
+# Indicates whether or not the STA supports link adaptation using VHT variant
+# HT Control field
+# If +HTC-VHTcapable is 1
+# 0 = (no feedback) if the STA does not provide VHT MFB (default)
+# 1 = reserved
+# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB
+# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the
+# STA provides unsolicited VHT MFB
+# Reserved if +HTC-VHTcapable is 0
+#
+# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN]
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+#
+# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN]
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an
+
+{% if capabilities.vht.center_channel_freq.freq_1 is defined %}
+# center freq = 5 GHz + (5 * index)
+# So index 42 gives center freq 5.210 GHz
+# which is channel 42 in 5G band
+vht_oper_centr_freq_seg0_idx={{ capabilities.vht.center_channel_freq.freq_1 }}
+{% endif %}
+
+{% if capabilities.vht.center_channel_freq.freq_2 is defined %}
+# center freq = 5 GHz + (5 * index)
+# So index 159 gives center freq 5.795 GHz
+# which is channel 159 in 5G band
+vht_oper_centr_freq_seg1_idx={{ capabilities.vht.center_channel_freq.freq_2 }}
+{% endif %}
+
+{% if capabilities.vht.channel_set_width is defined %}
+vht_oper_chwidth={{ capabilities.vht.channel_set_width }}
+{% endif %}
+
+{% set output = '' %}
+{% set output = output + '[TX-STBC-2BY1]' if capabilities.vht.stbc.tx is defined else '' %}
+{% set output = output + '[RXLDPC]' if capabilities.vht.ldpc is defined else '' %}
+{% set output = output + '[VHT-TXOP-PS]' if capabilities.vht.tx_powersave is defined else '' %}
+{% set output = output + '[HTC-VHT]' if capabilities.vht.vht_cf is defined else '' %}
+{% set output = output + '[RX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %}
+{% set output = output + '[TX-ANTENNA-PATTERN]' if capabilities.vht.antenna_pattern_fixed is defined else '' %}
+
+{% set output = output + '[RX-STBC-' + capabilities.vht.stbc.rx + ']' if capabilities.vht.stbc.rx is defined else '' %}
+{% set output = output + '[MAX-MPDU-' + capabilities.vht.max_mpdu + ']' if capabilities.vht.max_mpdu is defined else '' %}
+{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %}
+{% set output = output + '[MAX-A-MPDU-LEN-EXP-' + capabilities.vht.max_mpdu_exp + ']' if capabilities.vht.max_mpdu_exp is defined else '' %}
+
+{% set output = output + '[VHT160]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '2' else '' %}
+{% set output = output + '[VHT160-80PLUS80]' if capabilities.vht.max_mpdu_exp is defined and capabilities.vht.max_mpdu_exp == '3' else '' %}
+{% set output = output + '[VHT-LINK-ADAPT2]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'unsolicited' else '' %}
+{% set output = output + '[VHT-LINK-ADAPT3]' if capabilities.vht.link_adaptation is defined and capabilities.vht.link_adaptation == 'both' else '' %}
+
+{% if capabilities.vht.short_gi is defined %}
+{% for short_gi in capabilities.vht.short_gi %}
+{% set output = output + '[SHORT-GI-' + short_gi | upper + ']' %}
+{% endfor %}
+{% endif %}
+
+{% if capabilities.vht.beamform %}
+{% for beamform in capabilities.vht.beamform %}
+{% set output = output + '[SU-BEAMFORMER]' if beamform == 'single-user-beamformer' else '' %}
+{% set output = output + '[SU-BEAMFORMEE]' if beamform == 'single-user-beamformee' else '' %}
+{% set output = output + '[MU-BEAMFORMER]' if beamform == 'multi-user-beamformer' else '' %}
+{% set output = output + '[MU-BEAMFORMEE]' if beamform == 'multi-user-beamformee' else '' %}
+{% endfor %}
+{% endif %}
+
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 %}
+{% if capabilities.vht.beamform %}
+{% if beamform == 'single-user-beamformer' %}
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 6 %}
+{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count|int -1 + ']' %}
+{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count|int -1 + ']' %}
+{% endif %}
+{% endif %}
+{% if capabilities.vht.antenna_count is defined and capabilities.vht.antenna_count|int > 1 and capabilities.vht.antenna_count|int < 5 %}
+{% set output = output + '[BF-ANTENNA-' + capabilities.vht.antenna_count + ']' %}
+{% set output = output + '[SOUNDING-DIMENSION-' + capabilities.vht.antenna_count+ ']' %}
+{% endif %}
+{% endif %}
+{% endif %}
+
+vht_capab={{ output }}
+{% endif %}
+
+# ieee80211n: Whether IEEE 802.11n (HT) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+# Note: You will also need to enable WMM for full HT functionality.
+# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
+{% if capabilities is defined and capabilities.require_vht is defined %}
+ieee80211n=0
+# Require stations to support VHT PHY (reject association if they do not)
+require_vht=1
+{% else %}
+{% if 'n' in mode or 'ac' in mode %}
+ieee80211n=1
+{% else %}
+ieee80211n=0
+{% endif %}
+{% endif %}
+
+{% if disable_broadcast_ssid is defined %}
+# Send empty SSID in beacons and ignore probe request frames that do not
+# specify full SSID, i.e., require stations to know SSID.
+# default: disabled (0)
+# 1 = send empty (length=0) SSID in beacon and ignore probe request for
+# broadcast SSID
+# 2 = clear SSID (ASCII 0), but keep the original length (this may be required
+# with some clients that do not support empty SSID) and ignore probe
+# requests for broadcast SSID
+ignore_broadcast_ssid=1
+{% endif %}
+
+# Station MAC address -based authentication
+# Please note that this kind of access control requires a driver that uses
+# hostapd to take care of management frame processing and as such, this can be
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
+# 0 = accept unless in deny list
+# 1 = deny unless in accept list
+# 2 = use external RADIUS server (accept/deny lists are searched first)
+macaddr_acl=0
+
+{% if max_stations is defined %}
+# Maximum number of stations allowed in station table. New stations will be
+# rejected after the station table is full. IEEE 802.11 has a limit of 2007
+# different association IDs, so this number should not be larger than that.
+# (default: 2007)
+max_num_sta={{ max_stations }}
+{% endif %}
+
+{% if isolate_stations is defined %}
+# Client isolation can be used to prevent low-level bridging of frames between
+# associated stations in the BSS. By default, this bridging is allowed.
+ap_isolate=1
+{% endif %}
+
+{% if reduce_transmit_power is defined %}
+# Add Power Constraint element to Beacon and Probe Response frames
+# This config option adds Power Constraint element when applicable and Country
+# element is added. Power Constraint element is required by Transmit Power
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+local_pwr_constraint={{ reduce_transmit_power }}
+{% endif %}
+
+{% if expunge_failing_stations is defined %}
+# Disassociate stations based on excessive transmission failures or other
+# indications of connection loss. This depends on the driver capabilities and
+# may not be available with all drivers.
+disassoc_low_ack=1
+{% endif %}
+
+
+{% if security is defined and security.wep is defined %}
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=2
+
+# WEP rekeying (disabled if key lengths are not set or are set to 0)
+# Key lengths for default/broadcast and individual/unicast keys:
+# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits)
+# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits)
+wep_key_len_broadcast=5
+wep_key_len_unicast=5
+
+# Static WEP key configuration
+#
+# The key number to use when transmitting.
+# It must be between 0 and 3, and the corresponding key must be set.
+# default: not set
+wep_default_key=0
+
+# The WEP keys to use.
+# A key may be a quoted string or unquoted hexadecimal digits.
+# The key length should be 5, 13, or 16 characters, or 10, 26, or 32
+# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or
+# 128-bit (152-bit) WEP is used.
+# Only the default key must be supplied; the others are optional.
+{% if security.wep.key is defined %}
+{% for key in sec_wep_key %}
+wep_key{{ loop.index -1 }}={{ security.wep.key }}
+{% endfor %}
+{% endif %}
+
+
+{% elif security is defined and security.wpa is defined %}
+##### WPA/IEEE 802.11i configuration ##########################################
+
+# Enable WPA. Setting this variable configures the AP to require WPA (either
+# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either
+# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK.
+# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice.
+# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys),
+# RADIUS authentication server must be configured, and WPA-EAP must be included
+# in wpa_key_mgmt.
+# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0)
+# and/or WPA2 (full IEEE 802.11i/RSN):
+# bit0 = WPA
+# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled)
+{% if security.wpa.mode is defined %}
+{% if security.wpa.mode == 'both' %}
+wpa=3
+{% elif security.wpa.mode == 'wpa2' %}
+wpa=2
+{% elif security.wpa.mode == 'wpa' %}
+wpa=1
+{% endif %}
+{% endif %}
+
+{% if security.wpa.cipher is defined %}
+# Set of accepted cipher suites (encryption algorithms) for pairwise keys
+# (unicast packets). This is a space separated list of algorithms:
+# CCMP = AES in Counter mode with CBC-MAC (CCMP-128)
+# TKIP = Temporal Key Integrity Protocol
+# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key
+# GCMP = Galois/counter mode protocol (GCMP-128)
+# GCMP-256 = Galois/counter mode protocol with 256-bit key
+# Group cipher suite (encryption algorithm for broadcast and multicast frames)
+# is automatically selected based on this configuration. If only CCMP is
+# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise,
+# TKIP will be used as the group cipher. The optional group_cipher parameter can
+# be used to override this automatic selection.
+
+{% if security.wpa.mode is defined and security.wpa.mode == 'wpa2' %}
+# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value)
+{% if security.wpa.cipher is string %}
+rsn_pairwise={{ security.wpa.cipher }}
+{% else %}
+rsn_pairwise={{ security.wpa.cipher | join(" ") }}
+{% endif %}
+{% else %}
+# Pairwise cipher for WPA (v1) (default: TKIP)
+{% if security.wpa.cipher is string %}
+wpa_pairwise={{ security.wpa.cipher }}
+{% else %}
+wpa_pairwise={{ security.wpa.cipher | join(" ") }}
+{% endif %}
+{% endif %}
+{% endif %}
+
+{% if security.wpa.group_cipher is defined %}
+# Optional override for automatic group cipher selection
+# This can be used to select a specific group cipher regardless of which
+# pairwise ciphers were enabled for WPA and RSN. It should be noted that
+# overriding the group cipher with an unexpected value can result in
+# interoperability issues and in general, this parameter is mainly used for
+# testing purposes.
+{% if security.wpa.group_cipher is string %}
+group_cipher={{ security.wpa.group_cipher }}
+{% else %}
+group_cipher={{ security.wpa.group_cipher | join(" ") }}
+{% endif %}
+{% endif %}
+
+{% if security.wpa.passphrase is defined %}
+# IEEE 802.11 specifies two authentication algorithms. hostapd can be
+# configured to allow both of these or only one. Open system authentication
+# should be used with IEEE 802.1X.
+# Bit fields of allowed authentication algorithms:
+# bit 0 = Open System Authentication
+# bit 1 = Shared Key Authentication (requires WEP)
+auth_algs=1
+
+# WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
+# secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
+# (8..63 characters) that will be converted to PSK. This conversion uses SSID
+# so the PSK changes when ASCII passphrase is used and the SSID is changed.
+wpa_passphrase={{ security.wpa.passphrase }}
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+# added to enable SHA256-based stronger algorithms.
+# WPA-PSK = WPA-Personal / WPA2-Personal
+# WPA-PSK-SHA256 = WPA2-Personal using SHA256
+wpa_key_mgmt=WPA-PSK
+
+{% elif security.wpa.radius is defined %}
+##### IEEE 802.1X-2004 related configuration ##################################
+# Require IEEE 802.1X authorization
+ieee8021x=1
+
+# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
+# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
+# added to enable SHA256-based stronger algorithms.
+# WPA-EAP = WPA-Enterprise / WPA2-Enterprise
+# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256
+wpa_key_mgmt=WPA-EAP
+
+{% if security.wpa.radius.server is defined %}
+# RADIUS client forced local IP address for the access point
+# Normally the local IP address is determined automatically based on configured
+# IP addresses, but this field can be used to force a specific address to be
+# used, e.g., when the device has multiple IP addresses.
+# The own IP address of the access point (used as NAS-IP-Address)
+{% if security.wpa.radius.source_address is defined %}
+radius_client_addr={{ security.wpa.radius.source_address }}
+own_ip_addr={{ security.wpa.radius.source_address }}
+{% else %}
+own_ip_addr=127.0.0.1
+{% endif %}
+
+{% for radius in security.wpa.radius.server if not radius.disabled %}
+# RADIUS authentication server
+auth_server_addr={{ radius.server }}
+auth_server_port={{ radius.port }}
+auth_server_shared_secret={{ radius.key }}
+
+{% if radius.acc_port %}
+# RADIUS accounting server
+acct_server_addr={{ radius.server }}
+acct_server_port={{ radius.acc_port }}
+acct_server_shared_secret={{ radius.key }}
+{% endif %}
+{% endfor %}
+{% else %}
+# Open system
+auth_algs=1
+{% endif %}
+{% endif %}
+{% endif %}
+
+# TX queue parameters (EDCF / bursting)
+# tx_queue_<queue name>_<param>
+# queues: data0, data1, data2, data3
+# (data0 is the highest priority queue)
+# parameters:
+# aifs: AIFS (default 2)
+# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+# 16383, 32767)
+# cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
+# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
+# bursting
+#
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# These parameters are used by the access point when transmitting frames
+# to the clients.
+#
+# Low priority / AC_BK = background
+tx_queue_data3_aifs=7
+tx_queue_data3_cwmin=15
+tx_queue_data3_cwmax=1023
+tx_queue_data3_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0
+#
+# Normal priority / AC_BE = best effort
+tx_queue_data2_aifs=3
+tx_queue_data2_cwmin=15
+tx_queue_data2_cwmax=63
+tx_queue_data2_burst=0
+# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0
+#
+# High priority / AC_VI = video
+tx_queue_data1_aifs=1
+tx_queue_data1_cwmin=7
+tx_queue_data1_cwmax=15
+tx_queue_data1_burst=3.0
+# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0
+#
+# Highest priority / AC_VO = voice
+tx_queue_data0_aifs=1
+tx_queue_data0_cwmin=3
+tx_queue_data0_cwmax=7
+tx_queue_data0_burst=1.5
+
+# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e):
+# for 802.11a or 802.11g networks
+# These parameters are sent to WMM clients when they associate.
+# The parameters will be used by WMM clients for frames transmitted to the
+# access point.
+#
+# note - txop_limit is in units of 32microseconds
+# note - acm is admission control mandatory flag. 0 = admission control not
+# required, 1 = mandatory
+# note - Here cwMin and cmMax are in exponent form. The actual cw value used
+# will be (2^n)-1 where n is the value given here. The allowed range for these
+# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
+#
+wmm_enabled=1
+
+# Low priority / AC_BK = background
+wmm_ac_bk_cwmin=4
+wmm_ac_bk_cwmax=10
+wmm_ac_bk_aifs=7
+wmm_ac_bk_txop_limit=0
+wmm_ac_bk_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10
+#
+# Normal priority / AC_BE = best effort
+wmm_ac_be_aifs=3
+wmm_ac_be_cwmin=4
+wmm_ac_be_cwmax=10
+wmm_ac_be_txop_limit=0
+wmm_ac_be_acm=0
+# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7
+#
+# High priority / AC_VI = video
+wmm_ac_vi_aifs=2
+wmm_ac_vi_cwmin=3
+wmm_ac_vi_cwmax=4
+wmm_ac_vi_txop_limit=94
+wmm_ac_vi_acm=0
+# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188
+#
+# Highest priority / AC_VO = voice
+wmm_ac_vo_aifs=2
+wmm_ac_vo_cwmin=2
+wmm_ac_vo_cwmax=3
+wmm_ac_vo_txop_limit=47
+wmm_ac_vo_acm=0
+
diff --git a/data/templates/wifi/wpa_supplicant.conf.tmpl b/data/templates/wifi/wpa_supplicant.conf.tmpl
new file mode 100644
index 000000000..9ddad35fd
--- /dev/null
+++ b/data/templates/wifi/wpa_supplicant.conf.tmpl
@@ -0,0 +1,9 @@
+# WPA supplicant config
+network={
+ ssid="{{ ssid }}"
+{% if security is defined and security.wpa is defined and security.wpa.passphrase is defined %}
+ psk="{{ security.wpa.passphrase }}"
+{% else %}
+ key_mgmt=NONE
+{% endif %}
+}
diff --git a/data/templates/wwan/chat.tmpl b/data/templates/wwan/chat.tmpl
new file mode 100644
index 000000000..a3395c057
--- /dev/null
+++ b/data/templates/wwan/chat.tmpl
@@ -0,0 +1,6 @@
+ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT 'NO CARRIER' ABORT DELAYED
+'' AT
+OK ATZ
+OK 'AT+CGDCONT=1,"IP","{{ apn }}"'
+OK ATD*99#
+CONNECT ''
diff --git a/data/templates/wwan/ip-down.script.tmpl b/data/templates/wwan/ip-down.script.tmpl
new file mode 100644
index 000000000..9dc15ea99
--- /dev/null
+++ b/data/templates/wwan/ip-down.script.tmpl
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Script parameters will be like:
+# wlm0 /dev/serial/by-bus/usb0b1.3p1.3 115200 10.100.118.91 10.64.64.64 wlm0
+
+# Only applicable for Wireless Modems (WWAN)
+if [ -z $(echo $2 | egrep "(ttyS[0-9]+|usb[0-9]+b.*)$") ]; then
+ exit 0
+fi
+
+# Determine if we are running inside a VRF or not, required for proper routing table
+# NOTE: the down script can not be properly templated as we need the VRF name,
+# which is not present on deletion, thus we read it from the operating system.
+if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF_NAME=${VRF#"upper_"}
+ # Remove default route from VRF routing table
+ vtysh -c "conf t" -c "vrf ${VRF_NAME}" -c "no ip route 0.0.0.0/0 {{ ifname }}"
+else
+ # Remove default route from GRT (global routing table)
+ vtysh -c "conf t" -c "no ip route 0.0.0.0/0 {{ ifname }}"
+fi
+
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "removed default route via {{ ifname }} metric {{ backup.distance }}"
diff --git a/data/templates/wwan/ip-pre-up.script.tmpl b/data/templates/wwan/ip-pre-up.script.tmpl
new file mode 100644
index 000000000..efc065bad
--- /dev/null
+++ b/data/templates/wwan/ip-pre-up.script.tmpl
@@ -0,0 +1,23 @@
+#!/bin/sh
+# As WWAN is an "on demand" interface we need to re-configure it when it
+# becomes 'up'
+
+ipparam=$6
+
+# device name and metric are received using ipparam
+device=`echo "$ipparam"|awk '{ print $1 }'`
+
+if [ "$device" != "{{ ifname }}" ]; then
+ exit
+fi
+
+# add some info to syslog
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "executing $0"
+
+echo "{{ description }}" > /sys/class/net/{{ ifname }}/ifalias
+
+{% if vrf -%}
+logger -t pppd[$DIALER_PID] "configuring interface {{ ifname }} for VRF {{ vrf }}"
+ip link set dev {{ ifname }} master {{ vrf }}
+{% endif %}
diff --git a/data/templates/wwan/ip-up.script.tmpl b/data/templates/wwan/ip-up.script.tmpl
new file mode 100644
index 000000000..2603a0286
--- /dev/null
+++ b/data/templates/wwan/ip-up.script.tmpl
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Script parameters will be like:
+# wlm0 /dev/serial/by-bus/usb0b1.3p1.3 115200 10.100.118.91 10.64.64.64 wlm0
+
+# Only applicable for Wireless Modems (WWAN)
+if [ -z $(echo $2 | egrep "(ttyS[0-9]+|usb[0-9]+b.*)$") ]; then
+ exit 0
+fi
+
+# Determine if we are running inside a VRF or not, required for proper routing table
+if [ -d /sys/class/net/{{ ifname }}/upper_* ]; then
+ # Determine upper (VRF) interface
+ VRF=$(basename $(ls -d /sys/class/net/{{ ifname }}/upper_*))
+ # Remove upper_ prefix from result string
+ VRF_NAME=${VRF#"upper_"}
+ # Remove default route from VRF routing table
+ vtysh -c "conf t" -c "vrf ${VRF_NAME}" -c "ip route 0.0.0.0/0 {{ ifname }} {{ backup.distance }}"
+else
+ # Remove default route from GRT (global routing table)
+ vtysh -c "conf t" -c "ip route 0.0.0.0/0 {{ ifname }} {{ backup.distance }}"
+fi
+
+DIALER_PID=$(cat /var/run/{{ ifname }}.pid)
+logger -t pppd[$DIALER_PID] "added default route via {{ ifname }} metric {{ backup.distance }} ${VRF_NAME}"
diff --git a/data/templates/wwan/peer.tmpl b/data/templates/wwan/peer.tmpl
new file mode 100644
index 000000000..aa759f741
--- /dev/null
+++ b/data/templates/wwan/peer.tmpl
@@ -0,0 +1,27 @@
+### Autogenerated by interfaces-wirelessmodem.py ###
+
+{{ "# description: " + description if description is defined }}
+ifname {{ ifname }}
+ipparam {{ ifname }}
+linkname {{ ifname }}
+{{ "usepeerdns" if no_peer_dns is defined }}
+# physical device
+{{ device }}
+lcp-echo-failure 0
+115200
+debug
+debug
+mtu {{ mtu }}
+mru {{ mtu }}
+nodefaultroute
+ipcp-max-failure 4
+ipcp-accept-local
+ipcp-accept-remote
+noauth
+crtscts
+lock
+persist
+{{ "demand" if ondemand is defined }}
+
+connect '/usr/sbin/chat -v -t6 -f /etc/ppp/peers/chat.{{ ifname }}'
+