From 99c7c36ce16de656ed730b7a04fd55afceabb6dd Mon Sep 17 00:00:00 2001 From: William Fleurant Date: Sat, 11 Nov 2023 17:01:21 +0100 Subject: [PATCH] yggdrasil: overhaul package with netifd support - package is bumped to 0.5.2 - new protocol changes prevent peering with 0.4.x peers - @turretkeeper revamps package with netifd support - do not use with luci-app-yggdrasil please install luci-proto-yggdrasil Signed-off-by: William Fleurant --- net/yggdrasil/Makefile | 25 +-- net/yggdrasil/files/yggdrasil.defaults | 110 ------------- net/yggdrasil/files/yggdrasil.init | 33 ---- net/yggdrasil/files/yggdrasil.sh | 205 +++++++++++++++++++++++++ net/yggdrasil/files/ygguci | 155 ------------------- 5 files changed, 211 insertions(+), 317 deletions(-) delete mode 100644 net/yggdrasil/files/yggdrasil.defaults delete mode 100755 net/yggdrasil/files/yggdrasil.init create mode 100755 net/yggdrasil/files/yggdrasil.sh delete mode 100755 net/yggdrasil/files/ygguci diff --git a/net/yggdrasil/Makefile b/net/yggdrasil/Makefile index a40a9f5be8..32db0306eb 100644 --- a/net/yggdrasil/Makefile +++ b/net/yggdrasil/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=yggdrasil -PKG_VERSION:=0.4.7 +PKG_VERSION:=0.5.2 PKG_RELEASE:=1 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/yggdrasil-network/yggdrasil-go/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=47429f75b87d9b2450108471991e84c90d748606642e8778e9f578485b05a56f +PKG_HASH:=ed908594ab687e141dd2202e1b360e5bd93f910de1fd1f737d210cc784cf2470 PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-go-$(PKG_VERSION) PKG_MAINTAINER:=William Fleurant @@ -33,7 +33,7 @@ define Package/yggdrasil SUBMENU:=Routing and Redirection TITLE:=Yggdrasil supports end-to-end encrypted IPv6 networks URL:=https://yggdrasil-network.github.io/ - DEPENDS:=$(GO_ARCH_DEPENDS) @IPV6 +kmod-tun +dkjson +libuci-lua + DEPENDS:=$(GO_ARCH_DEPENDS) @IPV6 +kmod-tun endef define Package/yggdrasil/description @@ -46,14 +46,9 @@ define Package/yggdrasil/description interfaces simultaneously with much greater throughput. endef -define Package/yggdrasil/conffiles -/etc/config/yggdrasil -endef - define Package/yggdrasil/install $(INSTALL_DIR) \ - $(1)/etc/init.d \ - $(1)/etc/uci-defaults \ + $(1)/lib/netifd/proto \ $(1)/usr/sbin $(INSTALL_BIN) \ @@ -65,16 +60,8 @@ define Package/yggdrasil/install $(1)/usr/sbin $(INSTALL_BIN) \ - ./files/ygguci \ - $(1)/usr/sbin - - $(INSTALL_BIN) \ - ./files/yggdrasil.defaults \ - $(1)/etc/uci-defaults/yggdrasil - - $(INSTALL_BIN) \ - ./files/yggdrasil.init \ - $(1)/etc/init.d/yggdrasil + ./files/yggdrasil.sh \ + $(1)/lib/netifd/proto endef $(eval $(call GoBinPackage,yggdrasil)) diff --git a/net/yggdrasil/files/yggdrasil.defaults b/net/yggdrasil/files/yggdrasil.defaults deleted file mode 100644 index 2697262337..0000000000 --- a/net/yggdrasil/files/yggdrasil.defaults +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/sh - -yggConfig="/etc/config/yggdrasil" - -first_boot_genConfig() -{ - . /usr/share/libubox/jshn.sh - boardcfg=$(ubus call system board) - touch ${yggConfig} - yggdrasil -genconf -json | ygguci set - - json_load "$boardcfg" - json_get_var kernel kernel - json_get_var system system - json_get_var model model - json_get_var board_name board_name - nodeinfo='{"kernel": "'$kernel'", "hostname":"'OpenWrt'", "system": "'$system'", "model": "'$model'", "board_name": "'$board_name'"}' - - uci set yggdrasil.yggdrasil.IfName="ygg0" - uci set yggdrasil.yggdrasil.NodeInfo="$nodeinfo" - uci commit yggdrasil -} - -if [ -e /etc/yggdrasil.conf ]; then - echo "config: import config from /etc/yggdrasil.conf to /etc/config/yggdrasil" | logger -t yggdrasil - touch ${yggConfig} - cat /etc/yggdrasil.conf | ygguci set - mv /etc/yggdrasil.conf /etc/yggdrasil.conf.bak -elif [ ! -e ${yggConfig} ]; then - echo "first_boot: adding system board details to NodeInfo[] in NEW config: ${yggConfig}" | logger -t yggdrasil - - first_boot_genConfig - - # create the network interface - uci -q batch <<-EOF >/dev/null - set network.yggdrasil=interface - set network.yggdrasil.device=ygg0 - set network.yggdrasil.proto=none -EOF - - # create the firewall zone - uci -q batch <<-EOF >/dev/null - set firewall.yggdrasil=zone - set firewall.yggdrasil.name=yggdrasil - add_list firewall.yggdrasil.network=yggdrasil - set firewall.yggdrasil.input=REJECT - set firewall.yggdrasil.output=ACCEPT - set firewall.yggdrasil.forward=REJECT - set firewall.yggdrasil.conntrack=1 -EOF - - # allow ICMP from yggdrasil zone, e.g. ping6 - uci -q batch <<-EOF >/dev/null - add firewall rule - set firewall.@rule[-1].name='Allow-ICMPv6-yggdrasil' - set firewall.@rule[-1].src=yggdrasil - set firewall.@rule[-1].proto=icmp - add_list firewall.@rule[-1].icmp_type=echo-request - add_list firewall.@rule[-1].icmp_type=echo-reply - add_list firewall.@rule[-1].icmp_type=destination-unreachable - add_list firewall.@rule[-1].icmp_type=packet-too-big - add_list firewall.@rule[-1].icmp_type=time-exceeded - add_list firewall.@rule[-1].icmp_type=bad-header - add_list firewall.@rule[-1].icmp_type=unknown-header-type - set firewall.@rule[-1].limit='1000/sec' - set firewall.@rule[-1].family=ipv6 - set firewall.@rule[-1].target=ACCEPT -EOF - - # allow SSH from yggdrasil zone, needs to be explicitly enabled - uci -q batch <<-EOF >/dev/null - add firewall rule - set firewall.@rule[-1].enabled=0 - set firewall.@rule[-1].name='Allow-SSH-yggdrasil' - set firewall.@rule[-1].src=yggdrasil - set firewall.@rule[-1].proto=tcp - set firewall.@rule[-1].dest_port=22 - set firewall.@rule[-1].target=ACCEPT -EOF - - # allow LuCI access from yggdrasil zone, needs to be explicitly enabled - uci -q batch <<-EOF >/dev/null - add firewall rule - set firewall.@rule[-1].enabled=0 - set firewall.@rule[-1].name='Allow-HTTP-yggdrasil' - set firewall.@rule[-1].src=yggdrasil - set firewall.@rule[-1].proto=tcp - set firewall.@rule[-1].dest_port=80 - set firewall.@rule[-1].target=ACCEPT -EOF - - # allow LuCI access with SSL from yggdrasil zone, needs to be explicitly enabled - uci -q batch <<-EOF >/dev/null - add firewall rule - set firewall.@rule[-1].enabled=0 - set firewall.@rule[-1].name='Allow-HTTPS-yggdrasil' - set firewall.@rule[-1].src=yggdrasil - set firewall.@rule[-1].proto=tcp - set firewall.@rule[-1].dest_port=443 - set firewall.@rule[-1].target=ACCEPT -EOF - - uci commit firewall - uci commit network - -else - : -fi - -exit 0 diff --git a/net/yggdrasil/files/yggdrasil.init b/net/yggdrasil/files/yggdrasil.init deleted file mode 100755 index 3510e3a3ca..0000000000 --- a/net/yggdrasil/files/yggdrasil.init +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh /etc/rc.common - -START=90 -STOP=85 - -USE_PROCD=1 -BIN_FILE="/usr/sbin/yggdrasil" -CONFIG_FILE="/tmp/yggdrasil.conf" -DAEMON_OPTS="-useconffile $CONFIG_FILE" - -start_service() -{ - [ -f /etc/uci-defaults/yggdrasil ] && ( . /etc/uci-defaults/yggdrasil ) - - /usr/sbin/ygguci get | $BIN_FILE -useconf -normaliseconf -json > $CONFIG_FILE - - procd_open_instance - procd_set_param respawn - procd_set_param command $BIN_FILE $DAEMON_OPTS - procd_set_param stdout 1 - procd_set_param stderr 1 - procd_close_instance -} - -reload_service() -{ - restart -} - -service_triggers() -{ - procd_add_reload_trigger yggdrasil -} diff --git a/net/yggdrasil/files/yggdrasil.sh b/net/yggdrasil/files/yggdrasil.sh new file mode 100755 index 0000000000..a850920dc4 --- /dev/null +++ b/net/yggdrasil/files/yggdrasil.sh @@ -0,0 +1,205 @@ +#!/bin/sh + + +[ -n "$INCLUDE_ONLY" ] || { + . /lib/functions.sh + . ../netifd-proto.sh + init_proto "$@" +} + +proto_yggdrasil_init_config() { + proto_config_add_string "private_key" + available=1 +} + +proto_yggdrasil_setup_peer_if_non_interface() { + local peer_config="$1" + local peer_address + local peer_interface + config_get peer_address "${peer_config}" "address" + config_get peer_interface "${peer_config}" "interface" + if [ -z ${peer_interface} ]; then + json_add_string "" ${peer_address} + fi; +} + +proto_yggdrasil_dump_peer_interface() { + local peer_config="$1" + local peer_interface + + config_get peer_interface "${peer_config}" "interface" + + if [ ! -z ${peer_interface} ]; then + peer_interfaces="${peer_interfaces}\n${peer_interface}" + fi; +} + +proto_yggdrasil_setup_peer_if_interface() { + local peer_config="$1" + local peer_address + local peer_interface + config_get peer_interface "${peer_config}" "interface" + if [ "${peer_interface}" = "${peer_interface_filter}" ]; then + config_get peer_address "${peer_config}" "address" + json_add_string "" ${peer_address} + fi; +} + +proto_yggdrasil_append_to_interface_regex() { + if [ -z "${regex}" ]; then + regex="$1" + else + regex="${regex}|$1"; + fi; +} + +proto_yggdrasil_setup_multicast_interface() { + local interface_config="$1" + local beacon + local listen + local port=0 + local password + local regex="" + + config_get beacon "${interface_config}" "beacon" + config_get listen "${interface_config}" "listen" + config_get port "${interface_config}" "port" + config_get password "${interface_config}" "password" + + json_add_object "" + json_add_boolean "Beacon" $beacon + json_add_boolean "Listen" $listen + if [ ! -z ${port} ]; then + json_add_int "Port" $port + else + json_add_int "Port" 0 + fi; + if [ ! -z ${password} ]; then + json_add_string "Password" $password + fi; + + config_list_foreach "${interface_config}" interface proto_yggdrasil_append_to_interface_regex + + json_add_string "Regex" "^(${regex})\$" + + json_close_object +} + +proto_yggdrasil_add_string() { + json_add_string "" $1 +} + +proto_yggdrasil_generate_keypair() { + json_load "$(yggdrasil -genconf -json)" + json_get_vars PublicKey PrivateKey + json_cleanup + private_key=$PrivateKey + public_key=$PublicKey +} + +proto_yggdrasil_setup() { + local config="$1" + local device="$2" + local ygg_dir="/tmp/yggdrasil" + local ygg_cfg="${ygg_dir}/${config}.conf" + local ygg_sock="unix://${ygg_dir}/${config}.sock" + + + local private_key + local public_key + local mtu + local listen_addresses + local whitelisted_keys + local node_info + local node_info_privacy + + config_load network + config_get private_key "${config}" "private_key" + config_get public_key "${config}" "public_key" + config_get mtu "${config}" "mtu" + config_get node_info "${config}" "node_info" + config_get node_info_privacy "${config}" "node_info_privacy" + + if [ -z $private_key ]; then + proto_yggdrasil_generate_keypair + fi; + + umask 077 + mkdir -p "${ygg_dir}" + + if [ $private_key = "auto" ]; then + proto_yggdrasil_generate_keypair + uci -t ${ygg_dir}/.uci.${config} batch < "${ygg_cfg}.1" + awk -v s='"%%_YGGDRASIL_NODEINFO_TEMPLATE_%%"' -v r="${node_info}" '{gsub(s, r)} 1' "${ygg_cfg}.1" > ${ygg_cfg} + rm "${ygg_cfg}.1" + + proto_run_command "$config" /usr/sbin/yggdrasil -useconffile "${ygg_cfg}" + proto_init_update "$config" 1 + proto_add_ipv6_address "$(yggdrasil -useconffile "${ygg_cfg}" -address)" "7" + proto_add_ipv6_prefix "$(yggdrasil -useconffile "${ygg_cfg}" -subnet)" + proto_send_update "$config" +} + +proto_yggdrasil_teardown() { + local interface="$1" + proto_kill_command "$interface" +} + +[ -n "$INCLUDE_ONLY" ] || { + add_protocol yggdrasil +} diff --git a/net/yggdrasil/files/ygguci b/net/yggdrasil/files/ygguci deleted file mode 100755 index cdeb3c1a99..0000000000 --- a/net/yggdrasil/files/ygguci +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env lua - -dkjson = require("dkjson") -uci = require("uci") - -UCI = {} - ---- Return the configuration defaults as a table suitable for JSON output --- --- Mostly taken from yggdrasil -genconf -json --- @return table with configuration defaults -function UCI.defaults() - return { - AdminListen = "unix:///var/run/yggdrasil.sock", IfName = "ygg0", - NodeInfoPrivacy = false, - IfMTU = 65535, - - Peers = { }, Listen = { }, MulticastInterfaces = { }, AllowedPublicKeys = { }, - InterfacePeers = setmetatable({ }, {__jsontype = "object"}), - NodeInfo = setmetatable({ }, {__jsontype = "object"}) - } -end - ---- Return the yggdrasil configuration as a table suitable for JSON output --- --- @return table with yggdrasil configuration -function UCI.get() - local obj = UCI.defaults() - - local cursor = uci.cursor() - local config = cursor:get_all("yggdrasil", "yggdrasil") - if not config then return obj end - - obj.PublicKey = config.PublicKey - obj.PrivateKey = config.PrivateKey - obj.AdminListen = config.AdminListen or obj.AdminListen - obj.IfName = config.IfName or obj.IfName - obj.NodeInfo = dkjson.decode(config.NodeInfo) or obj.NodeInfo - for _, v in pairs({ "NodeInfoPrivacy" }) do - if config[v] ~= nil then obj[v] = to_bool(config[v]) end - end - if config["IfMTU"] ~= nil then obj["IfMTU"] = tonumber(config["IfMTU"]) end - - cursor:foreach("yggdrasil", "peer", function (s) - table.insert(obj.Peers, s.uri) - end) - cursor:foreach("yggdrasil", "listen_address", function (s) - table.insert(obj.Listen, s.uri) - end) - cursor:foreach("yggdrasil", "multicast_interface", function (s) - table.insert(obj.MulticastInterfaces, { - Beacon = to_bool(s.beacon), Listen = to_bool(s.listen), - Port = tonumber(s.port), Regex = s.regex - }) - end) - cursor:foreach("yggdrasil", "allowed_public_key", function (s) - table.insert(obj.AllowedPublicKeys, s.key) - end) - - cursor:foreach("yggdrasil", "interface_peer", function (s) - if obj.InterfacePeers[s.interface] == nil then - obj.InterfacePeers[s.interface] = {} - end - table.insert(obj.InterfacePeers[s["interface"]], s.uri) - end) - - return obj -end - ---- Parse and save updated configuration from JSON input --- --- Transforms general settings into UCI sections, and replaces the UCI config's --- contents with them. --- @param table JSON input --- @return Boolean whether saving succeeded -function UCI.set(obj) - local cursor = uci.cursor() - - for i, section in pairs(cursor:get_all("yggdrasil")) do - cursor:delete("yggdrasil", section[".name"]) - end - - - cursor:set("yggdrasil", "yggdrasil", "yggdrasil") - cursor:set("yggdrasil", "yggdrasil", "PublicKey", obj.PublicKey) - cursor:set("yggdrasil", "yggdrasil", "PrivateKey", obj.PrivateKey) - cursor:set("yggdrasil", "yggdrasil", "AdminListen", obj.AdminListen) - cursor:set("yggdrasil", "yggdrasil", "IfName", obj.IfName) - cursor:set("yggdrasil", "yggdrasil", "NodeInfoPrivacy", to_int(obj.NodeInfoPrivacy)) - cursor:set("yggdrasil", "yggdrasil", "NodeInfo", dkjson.encode(obj.NodeInfo)) - cursor:set("yggdrasil", "yggdrasil", "IfMTU", obj.IfMTU) - - set_values(cursor, "peer", "uri", obj.Peers) - set_values(cursor, "listen_address", "uri", obj.Listen) - - for _, interface in pairs(obj.MulticastInterfaces) do - local name = cursor:add("yggdrasil", "multicast_interface") - cursor:set("yggdrasil", name, "beacon", to_int(interface.Beacon)) - cursor:set("yggdrasil", name, "listen", to_int(interface.Listen)) - cursor:set("yggdrasil", name, "port", interface.Port) - cursor:set("yggdrasil", name, "regex", interface.Regex) - end - - set_values(cursor, "allowed_public_key", "key", obj.AllowedPublicKeys) - - for interface, peers in pairs(obj.InterfacePeers) do - for _, v in pairs(peers) do - local name = cursor:add("yggdrasil", "interface_peer") - cursor:set("yggdrasil", name, "interface", interface) - cursor:set("yggdrasil", name, "uri", v) - end - end - - return cursor:commit("yggdrasil") -end - -function set_values(cursor, section_name, parameter, values) - if values == nil then return false end - - for k, v in pairs(values) do - local name = cursor:add("yggdrasil", section_name) - cursor:set("yggdrasil", name, parameter, v) - end -end - -function to_int(bool) return bool and '1' or '0' end - -function to_bool(int) return int ~= '0' end - -function help() - print("JSON interface to /etc/config/yggdrasil\n\nExamples: \ - ygguci get > /tmp/etc/yggdrasil.conf \ - cat /tmp/etc/yggdrasil.conf | ygguci set \ - uci changes \ - ygguci get | yggdrasil -useconf") -end - --- main - -if arg[1] == "get" then - local json = dkjson.encode(UCI.get(), { indent = true }) - print(json) -elseif arg[1] == "set" then - local json = io.stdin:read("*a") - local obj, pos, err = dkjson.decode(json, 1, nil) - - if obj then - UCI.set(obj) - else - print("dkjson: " .. err) - os.exit(1) - end -else - help() -end -- 2.30.2