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 <meshnet@protonmail.com>
This commit is contained in:
William Fleurant 2023-11-11 17:01:21 +01:00
parent 298fd20375
commit 99c7c36ce1
5 changed files with 211 additions and 317 deletions

View File

@ -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 <meshnet@protonmail.com>
@ -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))

View File

@ -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

View File

@ -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
}

205
net/yggdrasil/files/yggdrasil.sh Executable file
View File

@ -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 <<EOF
set network.${config}.private_key='${private_key}'
set network.${config}.public_key='${public_key}'
EOF
uci -t ${ygg_dir}/.uci.${config} commit;
fi;
# Generate config file
json_init
json_add_string "IfName" ${config}
json_add_string "AdminListen" ${ygg_sock}
json_add_string "PrivateKey" ${private_key}
json_add_string "PublicKey" ${public_key}
if [ ! -z $mtu ]; then
json_add_int "IfMTU" ${mtu}
fi;
if [ ! -z $node_info ]; then
json_add_string "NodeInfo" "%%_YGGDRASIL_NODEINFO_TEMPLATE_%%"
fi;
json_add_boolean "NodeInfoPrivacy" ${node_info_privacy}
# Peers
json_add_array "Peers"
config_foreach proto_yggdrasil_setup_peer_if_non_interface "yggdrasil_${config}_peer"
json_close_array
local peer_interfaces
peer_interfaces=""
config_foreach proto_yggdrasil_dump_peer_interface "yggdrasil_${config}_peer"
peer_interfaces=$(echo -e ${peer_interfaces} | sort | uniq)
json_add_object "InterfacePeers"
for peer_interface_filter in ${peer_interfaces}; do
json_add_array "${peer_interface_filter}"
config_foreach proto_yggdrasil_setup_peer_if_interface "yggdrasil_${config}_peer"
json_close_array
done
json_close_object
json_add_array "AllowedPublicKeys"
config_list_foreach "$config" allowed_public_key proto_yggdrasil_add_string
json_close_array
json_add_array "Listen"
config_list_foreach "$config" listen_address proto_yggdrasil_add_string
json_close_array
json_add_array "MulticastInterfaces"
config_foreach proto_yggdrasil_setup_multicast_interface "yggdrasil_${config}_interface"
json_close_array
json_dump > "${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
}

View File

@ -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