Updates #8217 Signed-off-by: David Anderson <danderson@tailscale.com>main
parent
399a80785e
commit
32e0ba5e68
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,6 @@ |
||||
[Tailscale] |
||||
title="Tailscale" |
||||
desc="Tailscale VPN" |
||||
port_forward="no" |
||||
src.ports="41641/udp" |
||||
dst.ports="41641/udp" |
||||
@ -0,0 +1,12 @@ |
||||
{ |
||||
".url": { |
||||
"SYNO.SDS.Tailscale": { |
||||
"type": "url", |
||||
"version": "1.8.3", |
||||
"title": "Tailscale", |
||||
"icon": "PACKAGE_ICON_256.PNG", |
||||
"url": "webman/3rdparty/Tailscale/", |
||||
"urlTarget": "_syno_tailscale" |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,2 @@ |
||||
#! /bin/sh |
||||
exec /var/packages/Tailscale/target/bin/tailscale web -cgi |
||||
@ -0,0 +1,8 @@ |
||||
/var/packages/Tailscale/etc/tailscaled.stdout.log { |
||||
size 10M |
||||
rotate 3 |
||||
missingok |
||||
copytruncate |
||||
compress |
||||
notifempty |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
/var/packages/Tailscale/var/tailscaled.stdout.log { |
||||
size 10M |
||||
rotate 3 |
||||
missingok |
||||
copytruncate |
||||
compress |
||||
notifempty |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
{ |
||||
"defaults":{ |
||||
"run-as": "root" |
||||
}, |
||||
"username": "tailscale", |
||||
"groupname": "tailscale" |
||||
} |
||||
@ -0,0 +1,7 @@ |
||||
{ |
||||
"defaults":{ |
||||
"run-as": "package" |
||||
}, |
||||
"username": "tailscale", |
||||
"groupname": "tailscale" |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
{ |
||||
"defaults":{ |
||||
"run-as": "package" |
||||
}, |
||||
"username": "tailscale", |
||||
"groupname": "tailscale", |
||||
"tool": [{ |
||||
"relpath": "bin/tailscaled", |
||||
"user": "package", |
||||
"group": "package", |
||||
"capabilities": "cap_net_admin,cap_chown,cap_net_raw" |
||||
}] |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
{ |
||||
"port-config": { |
||||
"protocol-file": "conf/Tailscale.sc" |
||||
}, |
||||
"usr-local-linker": { |
||||
"bin": ["bin/tailscale"] |
||||
}, |
||||
"syslog-config": { |
||||
"logrotate-relpath": "conf/logrotate.conf" |
||||
} |
||||
} |
||||
@ -0,0 +1,3 @@ |
||||
#!/bin/sh |
||||
|
||||
exit 0 |
||||
@ -0,0 +1,3 @@ |
||||
#!/bin/sh |
||||
|
||||
exit 0 |
||||
@ -0,0 +1,129 @@ |
||||
#!/bin/bash |
||||
|
||||
SERVICE_NAME="tailscale" |
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then |
||||
PKGVAR="/var/packages/Tailscale/etc" |
||||
else |
||||
PKGVAR="${SYNOPKG_PKGVAR}" |
||||
fi |
||||
|
||||
PID_FILE="${PKGVAR}/tailscaled.pid" |
||||
LOG_FILE="${PKGVAR}/tailscaled.stdout.log" |
||||
STATE_FILE="${PKGVAR}/tailscaled.state" |
||||
SOCKET_FILE="${PKGVAR}/tailscaled.sock" |
||||
PORT="41641" |
||||
|
||||
SERVICE_COMMAND="${SYNOPKG_PKGDEST}/bin/tailscaled \ |
||||
--state=${STATE_FILE} \ |
||||
--socket=${SOCKET_FILE} \ |
||||
--port=$PORT" |
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" -a ! -e "/dev/net/tun" ]; then |
||||
# TODO(maisem/crawshaw): Disable the tun device in DSM7 for now. |
||||
SERVICE_COMMAND="${SERVICE_COMMAND} --tun=userspace-networking" |
||||
fi |
||||
|
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "6" ]; then |
||||
chown -R tailscale:tailscale "${PKGVAR}/" |
||||
fi |
||||
|
||||
start_daemon() { |
||||
local ts=$(date --iso-8601=second) |
||||
echo "${ts} Starting ${SERVICE_NAME} with: ${SERVICE_COMMAND}" >${LOG_FILE} |
||||
STATE_DIRECTORY=${PKGVAR} ${SERVICE_COMMAND} 2>&1 | sed -u '1,200p;201s,.*,[further tailscaled logs suppressed],p;d' >>${LOG_FILE} & |
||||
# We pipe tailscaled's output to sed, so "$!" retrieves the PID of sed not tailscaled. |
||||
# Use jobs -p to retrieve the PID of the most recent process group leader. |
||||
jobs -p >"${PID_FILE}" |
||||
} |
||||
|
||||
stop_daemon() { |
||||
if [ -r "${PID_FILE}" ]; then |
||||
local PID=$(cat "${PID_FILE}") |
||||
local ts=$(date --iso-8601=second) |
||||
echo "${ts} Stopping ${SERVICE_NAME} service PID=${PID}" >>${LOG_FILE} |
||||
kill -TERM $PID >>${LOG_FILE} 2>&1 |
||||
wait_for_status 1 || kill -KILL $PID >>${LOG_FILE} 2>&1 |
||||
rm -f "${PID_FILE}" >/dev/null |
||||
fi |
||||
} |
||||
|
||||
daemon_status() { |
||||
if [ -r "${PID_FILE}" ]; then |
||||
local PID=$(cat "${PID_FILE}") |
||||
if ps -o pid -p ${PID} > /dev/null; then |
||||
return |
||||
fi |
||||
rm -f "${PID_FILE}" >/dev/null |
||||
fi |
||||
return 1 |
||||
} |
||||
|
||||
wait_for_status() { |
||||
# 20 tries |
||||
# sleeps for 1 second after each try |
||||
local counter=20 |
||||
while [ ${counter} -gt 0 ]; do |
||||
daemon_status |
||||
[ $? -eq $1 ] && return |
||||
counter=$((counter - 1)) |
||||
sleep 1 |
||||
done |
||||
return 1 |
||||
} |
||||
|
||||
ensure_tun_created() { |
||||
if [ "${SYNOPKG_DSM_VERSION_MAJOR}" -eq "7" ]; then |
||||
# TODO(maisem/crawshaw): Disable the tun device in DSM7 for now. |
||||
return |
||||
fi |
||||
# Create the necessary file structure for /dev/net/tun |
||||
if ([ ! -c /dev/net/tun ]); then |
||||
if ([ ! -d /dev/net ]); then |
||||
mkdir -m 755 /dev/net |
||||
fi |
||||
mknod /dev/net/tun c 10 200 |
||||
chmod 0755 /dev/net/tun |
||||
fi |
||||
|
||||
# Load the tun module if not already loaded |
||||
if (!(lsmod | grep -q "^tun\s")); then |
||||
insmod /lib/modules/tun.ko |
||||
fi |
||||
} |
||||
|
||||
case $1 in |
||||
start) |
||||
if daemon_status; then |
||||
exit 0 |
||||
else |
||||
ensure_tun_created |
||||
start_daemon |
||||
exit $? |
||||
fi |
||||
;; |
||||
stop) |
||||
if daemon_status; then |
||||
stop_daemon |
||||
exit $? |
||||
else |
||||
exit 0 |
||||
fi |
||||
;; |
||||
status) |
||||
if daemon_status; then |
||||
echo "${SERVICE_NAME} is running" |
||||
exit 0 |
||||
else |
||||
echo "${SERVICE_NAME} is not running" |
||||
exit 3 |
||||
fi |
||||
;; |
||||
log) |
||||
exit 0 |
||||
;; |
||||
*) |
||||
echo "command $1 is not implemented" |
||||
exit 0 |
||||
;; |
||||
esac |
||||
@ -0,0 +1,306 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package synology contains dist Targets for building Synology Tailscale packages.
|
||||
package synology |
||||
|
||||
import ( |
||||
"archive/tar" |
||||
"bytes" |
||||
"compress/gzip" |
||||
"embed" |
||||
"fmt" |
||||
"io" |
||||
"io/fs" |
||||
"log" |
||||
"os" |
||||
"path/filepath" |
||||
"time" |
||||
|
||||
"tailscale.com/release/dist" |
||||
) |
||||
|
||||
type target struct { |
||||
filenameArch string |
||||
dsmMajorVersion int |
||||
goenv map[string]string |
||||
packageCenter bool |
||||
} |
||||
|
||||
func (t *target) String() string { |
||||
return fmt.Sprintf("synology/dsm%d/%s", t.dsmMajorVersion, t.filenameArch) |
||||
} |
||||
|
||||
func (t *target) Build(b *dist.Build) ([]string, error) { |
||||
inner, err := getSynologyBuilds(b).buildInnerPackage(b, t.dsmMajorVersion, t.goenv) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
out, err := t.buildSPK(b, inner) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return []string{out}, nil |
||||
} |
||||
|
||||
func (t *target) buildSPK(b *dist.Build, inner *innerPkg) (string, error) { |
||||
filename := fmt.Sprintf("tailscale-%s-%s-%d-dsm%d.spk", t.filenameArch, b.Version.Short, b.Version.Synology[t.dsmMajorVersion], t.dsmMajorVersion) |
||||
out := filepath.Join(b.Out, filename) |
||||
log.Printf("Building %s", filename) |
||||
|
||||
privFile := fmt.Sprintf("privilege-dsm%d", t.dsmMajorVersion) |
||||
if t.packageCenter && t.dsmMajorVersion == 7 { |
||||
privFile += ".for-package-center" |
||||
} |
||||
|
||||
f, err := os.Create(out) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
defer f.Close() |
||||
tw := tar.NewWriter(f) |
||||
defer tw.Close() |
||||
|
||||
err = writeTar(tw, b.Time, |
||||
memFile("INFO", t.mkInfo(b, inner.uncompressedSz), 0644), |
||||
static("PACKAGE_ICON.PNG", "PACKAGE_ICON.PNG", 0644), |
||||
static("PACKAGE_ICON_256.PNG", "PACKAGE_ICON_256.PNG", 0644), |
||||
static("Tailscale.sc", "Tailscale.sc", 0644), |
||||
dir("conf"), |
||||
static("resource", "conf/resource", 0644), |
||||
static(privFile, "conf/privilege", 0644), |
||||
file(inner.path, "package.tgz", 0644), |
||||
dir("scripts"), |
||||
static("scripts/start-stop-status", "scripts/start-stop-status", 0644), |
||||
static("scripts/postupgrade", "scripts/postupgrade", 0644), |
||||
static("scripts/preupgrade", "scripts/preupgrade", 0644), |
||||
) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
if err := tw.Close(); err != nil { |
||||
return "", err |
||||
} |
||||
if err := f.Close(); err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return out, nil |
||||
} |
||||
|
||||
func (t *target) mkInfo(b *dist.Build, uncompressedSz int64) []byte { |
||||
var ret bytes.Buffer |
||||
f := func(k, v string) { |
||||
fmt.Fprintf(&ret, "%s=%q\n", k, v) |
||||
} |
||||
f("package", "Tailscale") |
||||
f("version", fmt.Sprintf("%s-%d", b.Version.Short, b.Version.Synology[t.dsmMajorVersion])) |
||||
f("arch", t.filenameArch) |
||||
f("description", "Connect all your devices using WireGuard, without the hassle.") |
||||
f("displayname", "Tailscale") |
||||
f("maintainer", "Tailscale, Inc.") |
||||
f("maintainer_url", "https://github.com/tailscale/tailscale") |
||||
f("create_time", b.Time.Format("20060102-15:04:05")) |
||||
f("dsmuidir", "ui") |
||||
f("dsmappname", "SYNO.SDS.Tailscale") |
||||
f("startstop_restart_services", "nginx") |
||||
switch t.dsmMajorVersion { |
||||
case 6: |
||||
f("os_min_ver", "6.0.1-7445") |
||||
f("os_max_ver", "7.0-40000") |
||||
case 7: |
||||
f("os_min_ver", "7.0-40000") |
||||
f("os_max_ver", "") |
||||
default: |
||||
panic(fmt.Sprintf("unsupported DSM major version %d", t.dsmMajorVersion)) |
||||
} |
||||
f("extractsize", fmt.Sprintf("%v", uncompressedSz>>10)) // in KiB
|
||||
return ret.Bytes() |
||||
} |
||||
|
||||
type synologyBuildsMemoizeKey struct{} |
||||
|
||||
type innerPkg struct { |
||||
path string |
||||
uncompressedSz int64 |
||||
} |
||||
|
||||
// synologyBuilds is extra build context shared by all synology builds.
|
||||
type synologyBuilds struct { |
||||
innerPkgs dist.Memoize[*innerPkg] |
||||
} |
||||
|
||||
// getSynologyBuilds returns the synologyBuilds for b, creating one if needed.
|
||||
func getSynologyBuilds(b *dist.Build) *synologyBuilds { |
||||
return b.Extra(synologyBuildsMemoizeKey{}, func() any { return new(synologyBuilds) }).(*synologyBuilds) |
||||
} |
||||
|
||||
// buildInnerPackage builds the inner tarball for synology packages,
|
||||
// which contains the files to unpack to disk on installation (as
|
||||
// opposed to the outer tarball, which contains package metadata)
|
||||
func (m *synologyBuilds) buildInnerPackage(b *dist.Build, dsmVersion int, goenv map[string]string) (*innerPkg, error) { |
||||
key := []any{dsmVersion, goenv} |
||||
return m.innerPkgs.Do(key, func() (*innerPkg, error) { |
||||
ts, err := b.BuildGoBinary("tailscale.com/cmd/tailscale", goenv) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
tsd, err := b.BuildGoBinary("tailscale.com/cmd/tailscaled", goenv) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
tmp := b.TmpDir() |
||||
out := filepath.Join(tmp, "package.tgz") |
||||
|
||||
f, err := os.Create(out) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer f.Close() |
||||
gw := gzip.NewWriter(f) |
||||
defer gw.Close() |
||||
cw := &countingWriter{gw, 0} |
||||
tw := tar.NewWriter(cw) |
||||
defer tw.Close() |
||||
|
||||
err = writeTar(tw, b.Time, |
||||
dir("bin"), |
||||
file(tsd, "bin/tailscaled", 0755), |
||||
file(ts, "bin/tailscale", 0755), |
||||
dir("conf"), |
||||
static("Tailscale.sc", "conf/Tailscale.sc", 0644), |
||||
static(fmt.Sprintf("logrotate-dsm%d", dsmVersion), "conf/logrotate.conf", 0644), |
||||
dir("ui"), |
||||
static("PACKAGE_ICON_256.PNG", "ui/PACKAGE_ICON_256.PNG", 0644), |
||||
static("config", "ui/config", 0644), // TODO: this has "1.8.3" hard-coded in it; why? what is it? bug?
|
||||
static("index.cgi", "ui/index.cgi", 0755)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
if err := tw.Close(); err != nil { |
||||
return nil, err |
||||
} |
||||
if err := gw.Close(); err != nil { |
||||
return nil, err |
||||
} |
||||
if err := f.Close(); err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
return &innerPkg{out, cw.n}, nil |
||||
}) |
||||
} |
||||
|
||||
// writeTar writes ents to tw.
|
||||
func writeTar(tw *tar.Writer, modTime time.Time, ents ...tarEntry) error { |
||||
for _, ent := range ents { |
||||
if err := ent(tw, modTime); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// tarEntry is a function that writes tar entries (files or
|
||||
// directories) to a tar.Writer.
|
||||
type tarEntry func(*tar.Writer, time.Time) error |
||||
|
||||
// fsFile returns a tarEntry that writes src in fsys to dst in the tar
|
||||
// file, with mode.
|
||||
func fsFile(fsys fs.FS, src, dst string, mode int64) tarEntry { |
||||
return func(tw *tar.Writer, modTime time.Time) error { |
||||
f, err := fsys.Open(src) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer f.Close() |
||||
fi, err := f.Stat() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
hdr := &tar.Header{ |
||||
Name: dst, |
||||
Size: fi.Size(), |
||||
Mode: mode, |
||||
ModTime: modTime, |
||||
} |
||||
if err := tw.WriteHeader(hdr); err != nil { |
||||
return err |
||||
} |
||||
if _, err = io.Copy(tw, f); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
// file returns a tarEntry that writes src on disk into the tar file as
|
||||
// dst, with mode.
|
||||
func file(src, dst string, mode int64) tarEntry { |
||||
return fsFile(os.DirFS(filepath.Dir(src)), filepath.Base(src), dst, mode) |
||||
} |
||||
|
||||
//go:embed files/*
|
||||
var files embed.FS |
||||
|
||||
// static returns a tarEntry that writes src in files/ into the tar
|
||||
// file as dst, with mode.
|
||||
func static(src, dst string, mode int64) tarEntry { |
||||
fsys, err := fs.Sub(files, "files") |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
return fsFile(fsys, src, dst, mode) |
||||
} |
||||
|
||||
// memFile returns a tarEntry that writes bs to dst in the tar file,
|
||||
// with mode.
|
||||
func memFile(dst string, bs []byte, mode int64) tarEntry { |
||||
return func(tw *tar.Writer, modTime time.Time) error { |
||||
hdr := &tar.Header{ |
||||
Name: dst, |
||||
Size: int64(len(bs)), |
||||
Mode: mode, |
||||
ModTime: modTime, |
||||
} |
||||
if err := tw.WriteHeader(hdr); err != nil { |
||||
return err |
||||
} |
||||
if _, err := tw.Write(bs); err != nil { |
||||
return err |
||||
} |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
// dir returns a tarEntry that creates a world-readable directory in
|
||||
// the tar file.
|
||||
func dir(name string) tarEntry { |
||||
return func(tw *tar.Writer, modTime time.Time) error { |
||||
return tw.WriteHeader(&tar.Header{ |
||||
Typeflag: tar.TypeDir, |
||||
Name: name + "/", |
||||
Mode: 0755, |
||||
ModTime: modTime, |
||||
// TODO: why tailscale? Files are being written as owned by root.
|
||||
Uname: "tailscale", |
||||
Gname: "tailscale", |
||||
}) |
||||
} |
||||
} |
||||
|
||||
type countingWriter struct { |
||||
w io.Writer |
||||
n int64 |
||||
} |
||||
|
||||
func (cw *countingWriter) Write(bs []byte) (int, error) { |
||||
n, err := cw.w.Write(bs) |
||||
cw.n += int64(n) |
||||
return n, err |
||||
} |
||||
@ -0,0 +1,69 @@ |
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package synology |
||||
|
||||
import "tailscale.com/release/dist" |
||||
|
||||
func Targets(forPackageCenter bool) []dist.Target { |
||||
var ret []dist.Target |
||||
for _, dsmVersion := range []int{6, 7} { |
||||
ret = append(ret, |
||||
&target{ |
||||
filenameArch: "x86_64", |
||||
dsmMajorVersion: dsmVersion, |
||||
goenv: map[string]string{ |
||||
"GOOS": "linux", |
||||
"GOARCH": "amd64", |
||||
}, |
||||
packageCenter: forPackageCenter, |
||||
}, |
||||
&target{ |
||||
filenameArch: "i686", |
||||
dsmMajorVersion: dsmVersion, |
||||
goenv: map[string]string{ |
||||
"GOOS": "linux", |
||||
"GOARCH": "386", |
||||
}, |
||||
packageCenter: forPackageCenter, |
||||
}, |
||||
&target{ |
||||
filenameArch: "armv8", |
||||
dsmMajorVersion: dsmVersion, |
||||
goenv: map[string]string{ |
||||
"GOOS": "linux", |
||||
"GOARCH": "arm64", |
||||
}, |
||||
packageCenter: forPackageCenter, |
||||
}) |
||||
|
||||
// On older ARMv5 and ARMv7 platforms, synology used a whole
|
||||
// mess of SoC-specific target names, even though the packages
|
||||
// built for each are identical apart from metadata.
|
||||
for _, v5Arch := range []string{"armv5", "88f6281", "88f6282"} { |
||||
ret = append(ret, &target{ |
||||
filenameArch: v5Arch, |
||||
dsmMajorVersion: dsmVersion, |
||||
goenv: map[string]string{ |
||||
"GOOS": "linux", |
||||
"GOARCH": "arm", |
||||
"GOARM": "5", |
||||
}, |
||||
packageCenter: forPackageCenter, |
||||
}) |
||||
} |
||||
for _, v7Arch := range []string{"armv7", "alpine", "armada370", "armada375", "armada38x", "armadaxp", "comcerto2k", "monaco", "hi3535"} { |
||||
ret = append(ret, &target{ |
||||
filenameArch: v7Arch, |
||||
dsmMajorVersion: dsmVersion, |
||||
goenv: map[string]string{ |
||||
"GOOS": "linux", |
||||
"GOARCH": "arm", |
||||
"GOARM": "7", |
||||
}, |
||||
packageCenter: forPackageCenter, |
||||
}) |
||||
} |
||||
} |
||||
return ret |
||||
} |
||||
Loading…
Reference in new issue