mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-27 14:28:18 +00:00
Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7ac38e3e58 | ||
![]() |
49c424ef21 | ||
![]() |
0346af46da | ||
![]() |
93a5adfd18 | ||
![]() |
ddb75700a0 | ||
![]() |
ae997a5acb | ||
![]() |
6a9c90d3eb | ||
![]() |
41e045fe5b | ||
![]() |
e5e8c84d7c | ||
![]() |
e41b838d8f | ||
![]() |
7f9d4f3f6d | ||
![]() |
a6b316ef08 | ||
![]() |
d781fef760 | ||
![]() |
b332664acb | ||
![]() |
01c1498bd5 | ||
![]() |
0b578a637a | ||
![]() |
82c54f87ea | ||
![]() |
d17ac39789 | ||
![]() |
ea6ccf552f | ||
![]() |
1ac3d540e7 | ||
![]() |
6873fd44ff | ||
![]() |
8afa737a8d | ||
![]() |
7934158f5f | ||
![]() |
a60771344a |
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
@@ -119,6 +119,32 @@ jobs:
|
|||||||
- name: Unit tests
|
- name: Unit tests
|
||||||
run: go test -v ./...
|
run: go test -v ./...
|
||||||
|
|
||||||
|
build-freebsd:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
goversion: ["1.20", "1.21"]
|
||||||
|
goos:
|
||||||
|
- freebsd
|
||||||
|
- openbsd
|
||||||
|
|
||||||
|
name: Build (Cross ${{ matrix.goos }}, Go ${{ matrix.goversion }})
|
||||||
|
needs: [lint]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.goversion }}
|
||||||
|
|
||||||
|
- name: Build Yggdrasil
|
||||||
|
run: go build -v ./...
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
|
||||||
tests-ok:
|
tests-ok:
|
||||||
name: All tests passed
|
name: All tests passed
|
||||||
needs: [lint, codeql, build-linux, build-windows, build-macos]
|
needs: [lint, codeql, build-linux, build-windows, build-macos]
|
||||||
|
4
.github/workflows/pkg.yml
vendored
4
.github/workflows/pkg.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
name: Package (Debian, ${{ matrix.pkgarch }})
|
name: Package (Debian, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
|
|
||||||
name: Package (Router, ${{ matrix.pkgarch }})
|
name: Package (Router, ${{ matrix.pkgarch }})
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
|
26
CHANGELOG.md
26
CHANGELOG.md
@@ -26,22 +26,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||||||
- in case of vulnerabilities.
|
- in case of vulnerabilities.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## [0.5.0] - Release Candidate 3
|
## [0.5.2] - 2023-11-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* New `-publickey` command line option that prints the derived public key from a configuration file
|
||||||
|
* Support for connecting to TLS peers via SOCKS with the new `sockstls://` link schema
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Stabilise tree parent selection algorithm
|
||||||
|
* Improved logging when the TUN interface fails to set up
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Restored `removePeer` admin socket endpoint
|
* Fixed a panic that could occur when a connection reaches an inconsistent error state
|
||||||
* Fixed the `RetryPeersNow` API call for mobile
|
* The admin socket will now report more peering handshake error conditions in `getPeers`
|
||||||
|
* Yggdrasil will no longer panic at startup when duplicate peers are configured
|
||||||
|
* The `build` script will no longer incorrectly import `LDFLAGS` from the environment
|
||||||
|
|
||||||
## [0.5.0] - Release Candidate 2
|
## [0.5.1] - 2023-10-28
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* A bug which could result in high CPU usage after a network interface change has been fixed
|
* Fix the Debian package so that upgrades are handled more smoothly
|
||||||
* TLS listeners no longer require a TLS client certificate, as it is not necessary
|
|
||||||
* A panic in the mobile wrapper has been fixed when getting peers JSON
|
|
||||||
|
|
||||||
## [0.5.0] - Release Candidate 1
|
## [0.5.0] - 2023-10-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
@@ -48,8 +48,9 @@ func main() {
|
|||||||
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
autoconf := flag.Bool("autoconf", false, "automatic mode (dynamic IP, peer with IPv6 neighbors)")
|
||||||
ver := flag.Bool("version", false, "prints the version of this build")
|
ver := flag.Bool("version", false, "prints the version of this build")
|
||||||
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
logto := flag.String("logto", "stdout", "file path to log to, \"syslog\" or \"stdout\"")
|
||||||
getaddr := flag.Bool("address", false, "returns the IPv6 address as derived from the supplied configuration")
|
getaddr := flag.Bool("address", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 address")
|
||||||
getsnet := flag.Bool("subnet", false, "returns the IPv6 subnet as derived from the supplied configuration")
|
getsnet := flag.Bool("subnet", false, "use in combination with either -useconf or -useconffile, outputs your IPv6 subnet")
|
||||||
|
getpkey := flag.Bool("publickey", false, "use in combination with either -useconf or -useconffile, outputs your public key")
|
||||||
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
loglevel := flag.String("loglevel", "info", "loglevel to enable")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@@ -113,6 +114,7 @@ func main() {
|
|||||||
_ = f.Close()
|
_ = f.Close()
|
||||||
|
|
||||||
case *genconf:
|
case *genconf:
|
||||||
|
cfg.AdminListen = ""
|
||||||
var bs []byte
|
var bs []byte
|
||||||
if *confjson {
|
if *confjson {
|
||||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||||
@@ -154,7 +156,12 @@ func main() {
|
|||||||
fmt.Println(ipnet.String())
|
fmt.Println(ipnet.String())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
case *getpkey:
|
||||||
|
fmt.Println(hex.EncodeToString(publicKey))
|
||||||
|
return
|
||||||
|
|
||||||
case *normaliseconf:
|
case *normaliseconf:
|
||||||
|
cfg.AdminListen = ""
|
||||||
var bs []byte
|
var bs []byte
|
||||||
if *confjson {
|
if *confjson {
|
||||||
bs, err = json.MarshalIndent(cfg, "", " ")
|
bs, err = json.MarshalIndent(cfg, "", " ")
|
||||||
@@ -216,6 +223,9 @@ func main() {
|
|||||||
options := []admin.SetupOption{
|
options := []admin.SetupOption{
|
||||||
admin.ListenAddress(cfg.AdminListen),
|
admin.ListenAddress(cfg.AdminListen),
|
||||||
}
|
}
|
||||||
|
if cfg.LogLookups {
|
||||||
|
options = append(options, admin.LogLookups{})
|
||||||
|
}
|
||||||
if n.admin, err = admin.New(n.core, logger, options...); err != nil {
|
if n.admin, err = admin.New(n.core, logger, options...); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -39,8 +39,8 @@ func (cmdLineEnv *CmdLineEnv) parseFlagsAndArgs() {
|
|||||||
fmt.Println(" - ", os.Args[0], "list")
|
fmt.Println(" - ", os.Args[0], "list")
|
||||||
fmt.Println(" - ", os.Args[0], "getPeers")
|
fmt.Println(" - ", os.Args[0], "getPeers")
|
||||||
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
fmt.Println(" - ", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false")
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
|
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getPeers")
|
||||||
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
|
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getPeers")
|
||||||
}
|
}
|
||||||
|
|
||||||
server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint")
|
server := flag.String("endpoint", cmdLineEnv.endpoint, "Admin socket endpoint")
|
||||||
|
BIN
contrib/.DS_Store
vendored
Normal file
BIN
contrib/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -21,13 +21,16 @@ if [ $PKGBRANCH = "master" ]; then
|
|||||||
PKGREPLACES=yggdrasil-develop
|
PKGREPLACES=yggdrasil-develop
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build
|
GOLDFLAGS="-X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultConfig=/etc/yggdrasil/yggdrasil.conf"
|
||||||
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build
|
GOLDFLAGS="${GOLDFLAGS} -X github.com/yggdrasil-network/yggdrasil-go/src/config.defaultAdminListen=unix:///var/run/yggdrasil/yggdrasil.sock"
|
||||||
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build
|
|
||||||
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build
|
if [ $PKGARCH = "amd64" ]; then GOARCH=amd64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build
|
elif [ $PKGARCH = "i386" ]; then GOARCH=386 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build
|
elif [ $PKGARCH = "mipsel" ]; then GOARCH=mipsle GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build
|
elif [ $PKGARCH = "mips" ]; then GOARCH=mips64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
|
elif [ $PKGARCH = "armhf" ]; then GOARCH=arm GOOS=linux GOARM=6 ./build -l "${GOLDFLAGS}"
|
||||||
|
elif [ $PKGARCH = "arm64" ]; then GOARCH=arm64 GOOS=linux ./build -l "${GOLDFLAGS}"
|
||||||
|
elif [ $PKGARCH = "armel" ]; then GOARCH=arm GOOS=linux GOARM=5 ./build -l "${GOLDFLAGS}"
|
||||||
else
|
else
|
||||||
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
|
echo "Specify PKGARCH=amd64,i386,mips,mipsel,armhf,arm64,armel"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -38,7 +41,7 @@ echo "Building $PKGFILE"
|
|||||||
mkdir -p /tmp/$PKGNAME/
|
mkdir -p /tmp/$PKGNAME/
|
||||||
mkdir -p /tmp/$PKGNAME/debian/
|
mkdir -p /tmp/$PKGNAME/debian/
|
||||||
mkdir -p /tmp/$PKGNAME/usr/bin/
|
mkdir -p /tmp/$PKGNAME/usr/bin/
|
||||||
mkdir -p /tmp/$PKGNAME/etc/systemd/system/
|
mkdir -p /tmp/$PKGNAME/lib/systemd/system/
|
||||||
|
|
||||||
cat > /tmp/$PKGNAME/debian/changelog << EOF
|
cat > /tmp/$PKGNAME/debian/changelog << EOF
|
||||||
Please see https://github.com/yggdrasil-network/yggdrasil-go/
|
Please see https://github.com/yggdrasil-network/yggdrasil-go/
|
||||||
@@ -68,35 +71,52 @@ EOF
|
|||||||
cat > /tmp/$PKGNAME/debian/install << EOF
|
cat > /tmp/$PKGNAME/debian/install << EOF
|
||||||
usr/bin/yggdrasil usr/bin
|
usr/bin/yggdrasil usr/bin
|
||||||
usr/bin/yggdrasilctl usr/bin
|
usr/bin/yggdrasilctl usr/bin
|
||||||
etc/systemd/system/*.service etc/systemd/system
|
lib/systemd/system/*.service lib/systemd/system
|
||||||
EOF
|
EOF
|
||||||
cat > /tmp/$PKGNAME/debian/postinst << EOF
|
cat > /tmp/$PKGNAME/debian/postinst << EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
if ! getent group yggdrasil 2>&1 > /dev/null; then
|
if ! getent group yggdrasil 2>&1 > /dev/null; then
|
||||||
groupadd --system --force yggdrasil || echo "Failed to create group 'yggdrasil' - please create it manually and reinstall"
|
groupadd --system --force yggdrasil
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f /etc/yggdrasil.conf ];
|
if [ ! -d /etc/yggdrasil ];
|
||||||
|
then
|
||||||
|
mkdir -p /etc/yggdrasil
|
||||||
|
chown root:yggdrasil /etc/yggdrasil
|
||||||
|
chmod 750 /etc/yggdrasil
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /etc/yggdrasil/yggdrasil.conf ];
|
||||||
|
then
|
||||||
|
test -f /etc/yggdrasil.conf && mv /etc/yggdrasil.conf /etc/yggdrasil/yggdrasil.conf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/yggdrasil/yggdrasil.conf ];
|
||||||
then
|
then
|
||||||
mkdir -p /var/backups
|
mkdir -p /var/backups
|
||||||
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
|
echo "Backing up configuration file to /var/backups/yggdrasil.conf.`date +%Y%m%d`"
|
||||||
cp /etc/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
|
cp /etc/yggdrasil/yggdrasil.conf /var/backups/yggdrasil.conf.`date +%Y%m%d`
|
||||||
echo "Normalising and updating /etc/yggdrasil.conf"
|
|
||||||
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil.conf
|
|
||||||
chgrp yggdrasil /etc/yggdrasil.conf
|
|
||||||
|
|
||||||
if command -v systemctl >/dev/null; then
|
echo "Normalising and updating /etc/yggdrasil/yggdrasil.conf"
|
||||||
systemctl daemon-reload >/dev/null || true
|
/usr/bin/yggdrasil -useconf -normaliseconf < /var/backups/yggdrasil.conf.`date +%Y%m%d` > /etc/yggdrasil/yggdrasil.conf
|
||||||
systemctl enable yggdrasil || true
|
|
||||||
systemctl start yggdrasil || true
|
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
|
||||||
fi
|
chmod 640 /etc/yggdrasil/yggdrasil.conf
|
||||||
else
|
else
|
||||||
echo "Generating initial configuration file /etc/yggdrasil.conf"
|
echo "Generating initial configuration file /etc/yggdrasil/yggdrasil.conf"
|
||||||
echo "Please familiarise yourself with this file before starting Yggdrasil"
|
/usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf
|
||||||
sh -c 'umask 0027 && /usr/bin/yggdrasil -genconf > /etc/yggdrasil.conf'
|
|
||||||
chgrp yggdrasil /etc/yggdrasil.conf
|
chown root:yggdrasil /etc/yggdrasil/yggdrasil.conf
|
||||||
|
chmod 640 /etc/yggdrasil/yggdrasil.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
systemctl enable yggdrasil
|
||||||
|
systemctl restart yggdrasil
|
||||||
|
|
||||||
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
cat > /tmp/$PKGNAME/debian/prerm << EOF
|
cat > /tmp/$PKGNAME/debian/prerm << EOF
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
@@ -110,13 +130,14 @@ EOF
|
|||||||
|
|
||||||
cp yggdrasil /tmp/$PKGNAME/usr/bin/
|
cp yggdrasil /tmp/$PKGNAME/usr/bin/
|
||||||
cp yggdrasilctl /tmp/$PKGNAME/usr/bin/
|
cp yggdrasilctl /tmp/$PKGNAME/usr/bin/
|
||||||
cp contrib/systemd/*.service /tmp/$PKGNAME/etc/systemd/system/
|
cp contrib/systemd/yggdrasil-default-config.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil-default-config.service
|
||||||
|
cp contrib/systemd/yggdrasil.service.debian /tmp/$PKGNAME/lib/systemd/system/yggdrasil.service
|
||||||
|
|
||||||
tar -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \
|
tar --no-xattrs -czvf /tmp/$PKGNAME/data.tar.gz -C /tmp/$PKGNAME/ \
|
||||||
usr/bin/yggdrasil usr/bin/yggdrasilctl \
|
usr/bin/yggdrasil usr/bin/yggdrasilctl \
|
||||||
etc/systemd/system/yggdrasil.service \
|
lib/systemd/system/yggdrasil.service \
|
||||||
etc/systemd/system/yggdrasil-default-config.service
|
lib/systemd/system/yggdrasil-default-config.service
|
||||||
tar -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian .
|
tar --no-xattrs -czvf /tmp/$PKGNAME/control.tar.gz -C /tmp/$PKGNAME/debian .
|
||||||
echo 2.0 > /tmp/$PKGNAME/debian-binary
|
echo 2.0 > /tmp/$PKGNAME/debian-binary
|
||||||
|
|
||||||
ar -r $PKGFILE \
|
ar -r $PKGFILE \
|
||||||
|
@@ -48,6 +48,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
|||||||
logger.EnableLevel("error")
|
logger.EnableLevel("error")
|
||||||
logger.EnableLevel("warn")
|
logger.EnableLevel("warn")
|
||||||
logger.EnableLevel("info")
|
logger.EnableLevel("info")
|
||||||
|
m.logger = logger
|
||||||
m.config = config.GenerateConfig()
|
m.config = config.GenerateConfig()
|
||||||
if err := m.config.UnmarshalHJSON(configjson); err != nil {
|
if err := m.config.UnmarshalHJSON(configjson); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -87,6 +88,7 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
|||||||
// Setup the multicast module.
|
// Setup the multicast module.
|
||||||
if len(m.config.MulticastInterfaces) > 0 {
|
if len(m.config.MulticastInterfaces) > 0 {
|
||||||
var err error
|
var err error
|
||||||
|
logger.Infof("Initializing multicast %s", "")
|
||||||
options := []multicast.SetupOption{}
|
options := []multicast.SetupOption{}
|
||||||
for _, intf := range m.config.MulticastInterfaces {
|
for _, intf := range m.config.MulticastInterfaces {
|
||||||
options = append(options, multicast.MulticastInterface{
|
options = append(options, multicast.MulticastInterface{
|
||||||
@@ -98,9 +100,10 @@ func (m *Yggdrasil) StartJSON(configjson []byte) error {
|
|||||||
Password: intf.Password,
|
Password: intf.Password,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
logger.Infof("Starting multicast %s", "")
|
||||||
m.multicast, err = multicast.New(m.core, m.logger, options...)
|
m.multicast, err = multicast.New(m.core, m.logger, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Errorln("An error occurred starting multicast:", err)
|
logger.Errorln("An error occurred starting multicast:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,15 +162,20 @@ func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) {
|
|||||||
func (m *Yggdrasil) Stop() error {
|
func (m *Yggdrasil) Stop() error {
|
||||||
logger := log.New(m.log, "", 0)
|
logger := log.New(m.log, "", 0)
|
||||||
logger.EnableLevel("info")
|
logger.EnableLevel("info")
|
||||||
logger.Infof("Stop the mobile Yggdrasil instance %s", "")
|
logger.Infof("Stopping the mobile Yggdrasil instance %s", "")
|
||||||
|
if m.multicast != nil {
|
||||||
|
logger.Infof("Stopping multicast %s", "")
|
||||||
if err := m.multicast.Stop(); err != nil {
|
if err := m.multicast.Stop(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
logger.Infof("Stopping TUN device %s", "")
|
||||||
if m.tun != nil {
|
if m.tun != nil {
|
||||||
if err := m.tun.Stop(); err != nil {
|
if err := m.tun.Stop(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger.Infof("Stopping Yggdrasil core %s", "")
|
||||||
m.core.Stop()
|
m.core.Stop()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
13
contrib/systemd/yggdrasil-default-config.service.debian
Normal file
13
contrib/systemd/yggdrasil-default-config.service.debian
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Yggdrasil default config generator
|
||||||
|
ConditionPathExists=|!/etc/yggdrasil/yggdrasil.conf
|
||||||
|
ConditionFileNotEmpty=|!/etc/yggdrasil/yggdrasil.conf
|
||||||
|
Wants=local-fs.target
|
||||||
|
After=local-fs.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
Group=yggdrasil
|
||||||
|
ExecStartPre=/usr/bin/mkdir -p /etc/yggdrasil
|
||||||
|
ExecStart=/usr/bin/yggdrasil -genconf > /etc/yggdrasil/yggdrasil.conf
|
||||||
|
ExecStartPost=/usr/bin/chmod -R 0640 /etc/yggdrasil
|
25
contrib/systemd/yggdrasil.service.debian
Normal file
25
contrib/systemd/yggdrasil.service.debian
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Yggdrasil Network
|
||||||
|
Wants=network-online.target
|
||||||
|
Wants=yggdrasil-default-config.service
|
||||||
|
After=network-online.target
|
||||||
|
After=yggdrasil-default-config.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Group=yggdrasil
|
||||||
|
ProtectHome=true
|
||||||
|
ProtectSystem=strict
|
||||||
|
NoNewPrivileges=true
|
||||||
|
RuntimeDirectory=yggdrasil
|
||||||
|
ReadWritePaths=/var/run/yggdrasil/ /run/yggdrasil/
|
||||||
|
SyslogIdentifier=yggdrasil
|
||||||
|
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
|
||||||
|
ExecStartPre=+-/sbin/modprobe tun
|
||||||
|
ExecStart=/usr/bin/yggdrasil -useconffile /etc/yggdrasil/yggdrasil.conf
|
||||||
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
|
Restart=always
|
||||||
|
TimeoutStopSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
4
go.mod
4
go.mod
@@ -3,14 +3,14 @@ module github.com/yggdrasil-network/yggdrasil-go
|
|||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f
|
github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||||
github.com/cheggaaa/pb/v3 v3.1.4
|
github.com/cheggaaa/pb/v3 v3.1.4
|
||||||
github.com/gologme/log v1.3.0
|
github.com/gologme/log v1.3.0
|
||||||
github.com/hashicorp/go-syslog v1.0.0
|
github.com/hashicorp/go-syslog v1.0.0
|
||||||
github.com/hjson/hjson-go/v4 v4.3.0
|
github.com/hjson/hjson-go/v4 v4.3.0
|
||||||
github.com/kardianos/minwinsvc v1.0.2
|
github.com/kardianos/minwinsvc v1.0.2
|
||||||
github.com/quic-go/quic-go v0.39.0
|
github.com/quic-go/quic-go v0.39.3
|
||||||
github.com/vishvananda/netlink v1.1.0
|
github.com/vishvananda/netlink v1.1.0
|
||||||
golang.org/x/crypto v0.14.0
|
golang.org/x/crypto v0.14.0
|
||||||
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe
|
golang.org/x/mobile v0.0.0-20231006135142-2b44d11868fe
|
||||||
|
8
go.sum
8
go.sum
@@ -1,5 +1,5 @@
|
|||||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f h1:Fz0zG7ZyQQqk+ROnmHuGrIZO250Lx/YHmp9o48XE+Vw=
|
github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44 h1:u328GAZGtL0W4oFWQs4YWHZT215LL6Lw9aYJxx76UVs=
|
||||||
github.com/Arceliar/ironwood v0.0.0-20230805085300-86206813435f/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
github.com/Arceliar/ironwood v0.0.0-20231104025256-ec84c695fc44/go.mod h1:5x7fWW0mshe9WQ1lvSMmmHBYC3BeHH9gpwW5tz7cbfw=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||||
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||||
@@ -52,8 +52,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
|
||||||
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
|
||||||
github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So=
|
github.com/quic-go/quic-go v0.39.3 h1:o3YB6t2SR+HU/pgwF29kJ6g4jJIJEwEZ8CKia1h1TKg=
|
||||||
github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
github.com/quic-go/quic-go v0.39.3/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
|
||||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
@@ -1,9 +1,24 @@
|
|||||||
package admin
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Arceliar/ironwood/network"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
||||||
|
)
|
||||||
|
|
||||||
func (c *AdminSocket) _applyOption(opt SetupOption) {
|
func (c *AdminSocket) _applyOption(opt SetupOption) {
|
||||||
switch v := opt.(type) {
|
switch v := opt.(type) {
|
||||||
case ListenAddress:
|
case ListenAddress:
|
||||||
c.config.listenaddr = v
|
c.config.listenaddr = v
|
||||||
|
case LogLookups:
|
||||||
|
c.logLookups()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,3 +29,51 @@ type SetupOption interface {
|
|||||||
type ListenAddress string
|
type ListenAddress string
|
||||||
|
|
||||||
func (a ListenAddress) isSetupOption() {}
|
func (a ListenAddress) isSetupOption() {}
|
||||||
|
|
||||||
|
type LogLookups struct{}
|
||||||
|
|
||||||
|
func (l LogLookups) isSetupOption() {}
|
||||||
|
|
||||||
|
func (a *AdminSocket) logLookups() {
|
||||||
|
type resi struct {
|
||||||
|
Address string `json:"addr"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Path []uint64 `json:"path"`
|
||||||
|
Time int64 `json:"time"`
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
Infos []resi `json:"infos"`
|
||||||
|
}
|
||||||
|
type info struct {
|
||||||
|
path []uint64
|
||||||
|
time time.Time
|
||||||
|
}
|
||||||
|
type edk [ed25519.PublicKeySize]byte
|
||||||
|
infos := make(map[edk]info)
|
||||||
|
var m sync.Mutex
|
||||||
|
a.core.PacketConn.PacketConn.Debug.SetDebugLookupLogger(func(l network.DebugLookupInfo) {
|
||||||
|
var k edk
|
||||||
|
copy(k[:], l.Key[:])
|
||||||
|
m.Lock()
|
||||||
|
infos[k] = info{path: l.Path, time: time.Now()}
|
||||||
|
m.Unlock()
|
||||||
|
})
|
||||||
|
_ = a.AddHandler(
|
||||||
|
"lookups", "Dump a record of lookups received in the past hour", []string{},
|
||||||
|
func(in json.RawMessage) (interface{}, error) {
|
||||||
|
m.Lock()
|
||||||
|
rs := make([]resi, 0, len(infos))
|
||||||
|
for k, v := range infos {
|
||||||
|
if time.Since(v.time) > 24*time.Hour {
|
||||||
|
// TODO? automatic cleanup, so we don't need to call lookups periodically to prevent leaks
|
||||||
|
delete(infos, k)
|
||||||
|
}
|
||||||
|
a := address.AddrForKey(ed25519.PublicKey(k[:]))
|
||||||
|
addr := net.IP(a[:]).String()
|
||||||
|
rs = append(rs, resi{Address: addr, Key: hex.EncodeToString(k[:]), Path: v.path, Time: v.time.Unix()})
|
||||||
|
}
|
||||||
|
m.Unlock()
|
||||||
|
return &res{Infos: rs}, nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -46,13 +46,14 @@ type NodeConfig struct {
|
|||||||
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
Peers []string `comment:"List of connection strings for outbound peer connections in URI format,\ne.g. tls://a.b.c.d:e or socks://a.b.c.d:e/f.g.h.i:j. These connections\nwill obey the operating system routing table, therefore you should\nuse this section when you may connect via different interfaces."`
|
||||||
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
InterfacePeers map[string][]string `comment:"List of connection strings for outbound peer connections in URI format,\narranged by source interface, e.g. { \"eth0\": [ \"tls://a.b.c.d:e\" ] }.\nNote that SOCKS peerings will NOT be affected by this option and should\ngo in the \"Peers\" section instead."`
|
||||||
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
Listen []string `comment:"Listen addresses for incoming connections. You will need to add\nlisteners in order to accept incoming peerings from non-local nodes.\nMulticast peer discovery will work regardless of any listeners set\nhere. Each listener should be specified in URI format as above, e.g.\ntls://0.0.0.0:0 or tls://[::]:0 to listen on all interfaces."`
|
||||||
AdminListen string `comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
AdminListen string `json:",omitempty" comment:"Listen address for admin connections. Default is to listen for local\nconnections either on TCP/9001 or a UNIX socket depending on your\nplatform. Use this value for yggdrasilctl -endpoint=X. To disable\nthe admin socket, use the value \"none\" instead."`
|
||||||
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
MulticastInterfaces []MulticastInterfaceConfig `comment:"Configuration for which interfaces multicast peer discovery should be\nenabled on. Each entry in the list should be a json object which may\ncontain Regex, Beacon, Listen, and Port. Regex is a regular expression\nwhich is matched against an interface name, and interfaces use the\nfirst configuration that they match gainst. Beacon configures whether\nor not the node should send link-local multicast beacons to advertise\ntheir presence, while listening for incoming connections on Port.\nListen controls whether or not the node listens for multicast beacons\nand opens outgoing connections."`
|
||||||
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
AllowedPublicKeys []string `comment:"List of peer public keys to allow incoming peering connections\nfrom. If left empty/undefined then all connections will be allowed\nby default. This does not affect outgoing peerings, nor does it\naffect link-local peers discovered via multicast."`
|
||||||
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
IfName string `comment:"Local network interface name for TUN adapter, or \"auto\" to select\nan interface automatically, or \"none\" to run without TUN."`
|
||||||
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
IfMTU uint64 `comment:"Maximum Transmission Unit (MTU) size for your local TUN interface.\nDefault is the largest supported size for your platform. The lowest\npossible value is 1280."`
|
||||||
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
NodeInfoPrivacy bool `comment:"By default, nodeinfo contains some defaults including the platform,\narchitecture and Yggdrasil version. These can help when surveying\nthe network and diagnosing network routing problems. Enabling\nnodeinfo privacy prevents this, so that only items specified in\n\"NodeInfo\" are sent back if specified."`
|
||||||
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
NodeInfo map[string]interface{} `comment:"Optional node info. This must be a { \"key\": \"value\", ... } map\nor set as null. This is entirely optional but, if set, is visible\nto the whole network on request."`
|
||||||
|
LogLookups bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MulticastInterfaceConfig struct {
|
type MulticastInterfaceConfig struct {
|
||||||
|
@@ -179,6 +179,22 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
}
|
}
|
||||||
options.password = []byte(p)
|
options.password = []byte(p)
|
||||||
}
|
}
|
||||||
|
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
||||||
|
// that we do not populate the SNI with an IP literal. We do this by splitting
|
||||||
|
// the host-port combo from the query option and then seeing if it parses to an
|
||||||
|
// IP address successfully or not.
|
||||||
|
if sni := u.Query().Get("sni"); sni != "" {
|
||||||
|
if net.ParseIP(sni) == nil {
|
||||||
|
options.tlsSNI = sni
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the SNI is not configured still because the above failed then we'll try
|
||||||
|
// again but this time we'll use the host part of the peering URI instead.
|
||||||
|
if options.tlsSNI == "" {
|
||||||
|
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
|
||||||
|
options.tlsSNI = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we think we're already connected to this peer, load up
|
// If we think we're already connected to this peer, load up
|
||||||
// the existing peer state. Try to kick the peer if possible,
|
// the existing peer state. Try to kick the peer if possible,
|
||||||
@@ -257,7 +273,10 @@ func (l *links) add(u *url.URL, sintf string, linkType linkType) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
conn, err := l.connect(state.ctx, u, info, options)
|
conn, err := l.connect(state.ctx, u, info, options)
|
||||||
if err != nil {
|
if err != nil || conn == nil {
|
||||||
|
if err == nil && conn == nil {
|
||||||
|
l.core.log.Warnf("Link %q reached inconsistent error state", u.String())
|
||||||
|
}
|
||||||
if linkType == linkTypePersistent {
|
if linkType == linkTypePersistent {
|
||||||
// If the link is a persistent configured peering,
|
// If the link is a persistent configured peering,
|
||||||
// store information about the connection error so
|
// store information about the connection error so
|
||||||
@@ -497,24 +516,8 @@ func (l *links) connect(ctx context.Context, u *url.URL, info linkInfo, options
|
|||||||
case "tcp":
|
case "tcp":
|
||||||
dialer = l.tcp
|
dialer = l.tcp
|
||||||
case "tls":
|
case "tls":
|
||||||
// SNI headers must contain hostnames and not IP addresses, so we must make sure
|
|
||||||
// that we do not populate the SNI with an IP literal. We do this by splitting
|
|
||||||
// the host-port combo from the query option and then seeing if it parses to an
|
|
||||||
// IP address successfully or not.
|
|
||||||
if sni := u.Query().Get("sni"); sni != "" {
|
|
||||||
if net.ParseIP(sni) == nil {
|
|
||||||
options.tlsSNI = sni
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the SNI is not configured still because the above failed then we'll try
|
|
||||||
// again but this time we'll use the host part of the peering URI instead.
|
|
||||||
if options.tlsSNI == "" {
|
|
||||||
if host, _, err := net.SplitHostPort(u.Host); err == nil && net.ParseIP(host) == nil {
|
|
||||||
options.tlsSNI = host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dialer = l.tls
|
dialer = l.tls
|
||||||
case "socks":
|
case "socks", "sockstls":
|
||||||
dialer = l.socks
|
dialer = l.socks
|
||||||
case "unix":
|
case "unix":
|
||||||
dialer = l.unix
|
dialer = l.unix
|
||||||
@@ -546,8 +549,9 @@ func (l *links) handler(linkType linkType, options linkOptions, conn net.Conn) e
|
|||||||
}
|
}
|
||||||
meta = version_metadata{}
|
meta = version_metadata{}
|
||||||
base := version_getBaseMetadata()
|
base := version_getBaseMetadata()
|
||||||
if !meta.decode(conn, options.password) {
|
if err := meta.decode(conn, options.password); err != nil {
|
||||||
return conn.Close()
|
_ = conn.Close()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if !meta.check() {
|
if !meta.check() {
|
||||||
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
|
return fmt.Errorf("remote node incompatible version (local %s, remote %s)",
|
||||||
|
@@ -2,6 +2,7 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
@@ -34,7 +35,16 @@ func (l *linkSOCKS) dial(_ context.Context, url *url.URL, info linkInfo, options
|
|||||||
return nil, fmt.Errorf("failed to configure proxy")
|
return nil, fmt.Errorf("failed to configure proxy")
|
||||||
}
|
}
|
||||||
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
pathtokens := strings.Split(strings.Trim(url.Path, "/"), "/")
|
||||||
return dialer.Dial("tcp", pathtokens[0])
|
conn, err := dialer.Dial("tcp", pathtokens[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to dial: %w", err)
|
||||||
|
}
|
||||||
|
if url.Scheme == "sockstls" {
|
||||||
|
tlsconfig := l.tls.config.Clone()
|
||||||
|
tlsconfig.ServerName = options.tlsSNI
|
||||||
|
conn = tls.Client(conn, tlsconfig)
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
func (l *linkSOCKS) listen(ctx context.Context, url *url.URL, _ string) (net.Listener, error) {
|
||||||
|
@@ -13,7 +13,15 @@ func (c *Core) _applyOption(opt SetupOption) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to parse peering URI: %w", err)
|
return fmt.Errorf("unable to parse peering URI: %w", err)
|
||||||
}
|
}
|
||||||
return c.links.add(u, v.SourceInterface, linkTypePersistent)
|
err = c.links.add(u, v.SourceInterface, linkTypePersistent)
|
||||||
|
switch err {
|
||||||
|
case ErrLinkAlreadyConfigured:
|
||||||
|
// Don't return this error, otherwise we'll panic at startup
|
||||||
|
// if there are multiple of the same peer configured
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
case ListenAddress:
|
case ListenAddress:
|
||||||
c.config._listeners[v] = struct{}{}
|
c.config._listeners[v] = struct{}{}
|
||||||
case NodeInfo:
|
case NodeInfo:
|
||||||
|
41
src/core/options_test.go
Normal file
41
src/core/options_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/yggdrasil-network/yggdrasil-go/src/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests that duplicate peers in the configuration file
|
||||||
|
// won't cause an error when the node starts. Otherwise
|
||||||
|
// we can panic unnecessarily.
|
||||||
|
func TestDuplicatePeerAtStartup(t *testing.T) {
|
||||||
|
cfg := config.GenerateConfig()
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
cfg.Peers = append(cfg.Peers, "tcp://1.2.3.4:4321")
|
||||||
|
}
|
||||||
|
if _, err := New(cfg.Certificate, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that duplicate peers given to us through the
|
||||||
|
// API will still error as expected, even if they didn't
|
||||||
|
// at startup. We expect to notify the user through the
|
||||||
|
// admin socket if they try to add a peer that is already
|
||||||
|
// configured.
|
||||||
|
func TestDuplicatePeerFromAPI(t *testing.T) {
|
||||||
|
cfg := config.GenerateConfig()
|
||||||
|
c, err := New(cfg.Certificate, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
u, _ := url.Parse("tcp://1.2.3.4:4321")
|
||||||
|
if err := c.AddPeer(u, ""); err != nil {
|
||||||
|
t.Fatalf("Adding peer failed on first attempt: %s", err)
|
||||||
|
}
|
||||||
|
if err := c.AddPeer(u, ""); err == nil {
|
||||||
|
t.Fatalf("Adding peer should have failed on second attempt")
|
||||||
|
}
|
||||||
|
}
|
@@ -87,22 +87,22 @@ func (m *version_metadata) encode(privateKey ed25519.PrivateKey, password []byte
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decodes version metadata from its wire format into the struct.
|
// Decodes version metadata from its wire format into the struct.
|
||||||
func (m *version_metadata) decode(r io.Reader, password []byte) bool {
|
func (m *version_metadata) decode(r io.Reader, password []byte) error {
|
||||||
bh := [6]byte{}
|
bh := [6]byte{}
|
||||||
if _, err := io.ReadFull(r, bh[:]); err != nil {
|
if _, err := io.ReadFull(r, bh[:]); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
meta := [4]byte{'m', 'e', 't', 'a'}
|
meta := [4]byte{'m', 'e', 't', 'a'}
|
||||||
if !bytes.Equal(bh[:4], meta[:]) {
|
if !bytes.Equal(bh[:4], meta[:]) {
|
||||||
return false
|
return fmt.Errorf("invalid handshake preamble")
|
||||||
}
|
}
|
||||||
bs := make([]byte, binary.BigEndian.Uint16(bh[4:6]))
|
hl := binary.BigEndian.Uint16(bh[4:6])
|
||||||
|
if hl < ed25519.SignatureSize {
|
||||||
|
return fmt.Errorf("invalid handshake length")
|
||||||
|
}
|
||||||
|
bs := make([]byte, hl)
|
||||||
if _, err := io.ReadFull(r, bs); err != nil {
|
if _, err := io.ReadFull(r, bs); err != nil {
|
||||||
return false
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if len(bs) < ed25519.SignatureSize {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
sig := bs[len(bs)-ed25519.SignatureSize:]
|
sig := bs[len(bs)-ed25519.SignatureSize:]
|
||||||
bs = bs[:len(bs)-ed25519.SignatureSize]
|
bs = bs[:len(bs)-ed25519.SignatureSize]
|
||||||
@@ -132,14 +132,17 @@ func (m *version_metadata) decode(r io.Reader, password []byte) bool {
|
|||||||
|
|
||||||
hasher, err := blake2b.New512(password)
|
hasher, err := blake2b.New512(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return fmt.Errorf("invalid password supplied")
|
||||||
}
|
}
|
||||||
n, err := hasher.Write(m.publicKey)
|
n, err := hasher.Write(m.publicKey)
|
||||||
if err != nil || n != ed25519.PublicKeySize {
|
if err != nil || n != ed25519.PublicKeySize {
|
||||||
return false
|
return fmt.Errorf("failed to generate hash")
|
||||||
}
|
}
|
||||||
hash := hasher.Sum(nil)
|
hash := hasher.Sum(nil)
|
||||||
return ed25519.Verify(m.publicKey, hash, sig)
|
if !ed25519.Verify(m.publicKey, hash, sig) {
|
||||||
|
return fmt.Errorf("password is incorrect")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that the "meta" bytes and the version numbers are the expected values.
|
// Checks that the "meta" bytes and the version numbers are the expected values.
|
||||||
|
@@ -34,7 +34,7 @@ func TestVersionPasswordAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var decoded version_metadata
|
var decoded version_metadata
|
||||||
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2); allowed != tt.allowed {
|
if allowed := decoded.decode(bytes.NewBuffer(encoded), tt.password2) == nil; allowed != tt.allowed {
|
||||||
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
|
t.Fatalf("Permutation %q -> %q should have been %v but was %v", tt.password1, tt.password2, tt.allowed, allowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,8 +67,8 @@ func TestVersionRoundtrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
encoded := bytes.NewBuffer(meta)
|
encoded := bytes.NewBuffer(meta)
|
||||||
decoded := &version_metadata{}
|
decoded := &version_metadata{}
|
||||||
if !decoded.decode(encoded, password) {
|
if err := decoded.decode(encoded, password); err != nil {
|
||||||
t.Fatalf("failed to decode")
|
t.Fatalf("failed to decode: %s", err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(test, decoded) {
|
if !reflect.DeepEqual(test, decoded) {
|
||||||
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
t.Fatalf("round-trip failed\nwant: %+v\n got: %+v", test, decoded)
|
||||||
|
@@ -78,7 +78,7 @@ type in6_ifreq_lifetime struct {
|
|||||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
|
@@ -7,6 +7,7 @@ package tun
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -24,7 +25,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if m, err := iface.MTU(); err == nil {
|
if m, err := iface.MTU(); err == nil {
|
||||||
@@ -42,17 +43,17 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
||||||
dfd, err := unix.Dup(int(fd))
|
dfd, err := unix.Dup(int(fd))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to duplicate FD: %w", err)
|
||||||
}
|
}
|
||||||
err = unix.SetNonblock(dfd, true)
|
err = unix.SetNonblock(dfd, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(dfd)
|
unix.Close(dfd)
|
||||||
return err
|
return fmt.Errorf("failed to set FD as non-blocking: %w", err)
|
||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
|
iface, err := wgtun.CreateTUNFromFile(os.NewFile(uintptr(dfd), "/dev/tun"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unix.Close(dfd)
|
unix.Close(dfd)
|
||||||
return err
|
return fmt.Errorf("failed to create TUN from FD: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if m, err := iface.MTU(); err == nil {
|
if m, err := iface.MTU(); err == nil {
|
||||||
@@ -111,7 +112,7 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
|||||||
|
|
||||||
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
if fd, err = unix.Socket(unix.AF_INET6, unix.SOCK_DGRAM, 0); err != nil {
|
||||||
tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err)
|
tun.log.Printf("Create AF_SYSTEM socket failed: %v.", err)
|
||||||
return err
|
return fmt.Errorf("failed to open AF_SYSTEM: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ar in6_aliasreq
|
var ar in6_aliasreq
|
||||||
@@ -149,13 +150,13 @@ func (tun *TunAdapter) setupAddress(addr string) error {
|
|||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(darwin_SIOCAIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { // nolint:staticcheck
|
||||||
err = errno
|
err = errno
|
||||||
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
tun.log.Errorf("Error in darwin_SIOCAIFADDR_IN6: %v", errno)
|
||||||
return err
|
return fmt.Errorf("failed to call SIOCAIFADDR_IN6: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
|
if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(unix.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { // nolint:staticcheck
|
||||||
err = errno
|
err = errno
|
||||||
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
tun.log.Errorf("Error in SIOCSIFMTU: %v", errno)
|
||||||
return err
|
return fmt.Errorf("failed to call SIOCSIFMTU: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
@@ -19,7 +19,7 @@ func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
|||||||
}
|
}
|
||||||
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
iface, err := wgtun.CreateTUN(ifname, int(mtu))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
@@ -45,20 +45,20 @@ func (tun *TunAdapter) setupFD(fd int32, addr string, mtu uint64) error {
|
|||||||
func (tun *TunAdapter) setupAddress(addr string) error {
|
func (tun *TunAdapter) setupAddress(addr string) error {
|
||||||
nladdr, err := netlink.ParseAddr(addr)
|
nladdr, err := netlink.ParseAddr(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("couldn't parse address %q: %w", addr, err)
|
||||||
}
|
}
|
||||||
nlintf, err := netlink.LinkByName(tun.Name())
|
nlintf, err := netlink.LinkByName(tun.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to find link by name: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
|
if err := netlink.AddrAdd(nlintf, nladdr); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to add address to link: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
|
if err := netlink.LinkSetMTU(nlintf, int(tun.mtu)); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to set link MTU: %w", err)
|
||||||
}
|
}
|
||||||
if err := netlink.LinkSetUp(nlintf); err != nil {
|
if err := netlink.LinkSetUp(nlintf); err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to bring link up: %w", err)
|
||||||
}
|
}
|
||||||
// Friendly output
|
// Friendly output
|
||||||
tun.log.Infof("Interface name: %s", tun.Name())
|
tun.log.Infof("Interface name: %s", tun.Name())
|
||||||
|
@@ -16,7 +16,7 @@ import (
|
|||||||
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
func (tun *TunAdapter) setup(ifname string, addr string, mtu uint64) error {
|
||||||
iface, err := wgtun.CreateTUN(ifname, mtu)
|
iface, err := wgtun.CreateTUN(ifname, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return fmt.Errorf("failed to create TUN: %w", err)
|
||||||
}
|
}
|
||||||
tun.iface = iface
|
tun.iface = iface
|
||||||
if mtu, err := iface.MTU(); err == nil {
|
if mtu, err := iface.MTU(); err == nil {
|
||||||
|
Reference in New Issue
Block a user