Skip to content

Commit e245c15

Browse files
authored
Merge branch 'main' into supported-protos
2 parents 8d70443 + 4eb3658 commit e245c15

File tree

11 files changed

+367
-0
lines changed

11 files changed

+367
-0
lines changed

.github/workflows/release.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
on:
2+
push:
3+
tags:
4+
- "v*"
5+
6+
permissions:
7+
contents: write
8+
id-token: "write"
9+
10+
jobs:
11+
release:
12+
runs-on:
13+
group: large-runners
14+
steps:
15+
- name: Checkout
16+
uses: actions/checkout@v4
17+
with:
18+
fetch-depth: 0 # Mandatory to use the extract version from tag
19+
- uses: actions/setup-go@v5
20+
with:
21+
go-version-file: "go.mod"
22+
- name: Login to Docker Hub
23+
uses: docker/login-action@v3
24+
with:
25+
username: getlantern
26+
password: ${{ secrets.DOCKER_PASSWORD }}
27+
28+
- name: Run GoReleaser
29+
uses: goreleaser/goreleaser-action@v6
30+
with:
31+
distribution: goreleaser-pro
32+
version: "~> 2"
33+
args: release --clean --verbose
34+
env:
35+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
37+
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
38+
FURY_TOKEN: ${{ secrets.FURY_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/data/
2+
/dist/

.goreleaser.yaml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
2+
version: 2
3+
project_name: sing-box-extensions
4+
metadata:
5+
maintainers:
6+
- "Lantern Team <[email protected]>"
7+
description: Sing Box Extensions
8+
homepage: "https://github.com/getlantern/sing-box-extensions"
9+
license: "GPL"
10+
11+
builds:
12+
- main: ./cmd/sing-box-extensions
13+
flags:
14+
- -v
15+
- -trimpath
16+
goos:
17+
- linux
18+
goarch:
19+
- amd64
20+
env:
21+
- CGO_ENABLED=1
22+
ldflags:
23+
- -X github.com/sagernet/sing-box/constant.Version={{ .Version }} -s -w -buildid=
24+
tags:
25+
- with_gvisor
26+
- with_quic
27+
- with_dhcp
28+
- with_wireguard
29+
- with_ech
30+
- with_utls
31+
- with_reality_server
32+
- with_clash_api
33+
34+
binary: sing-box-extensions
35+
36+
archives:
37+
- formats: ["tar.gz"]
38+
39+
changelog:
40+
sort: asc
41+
filters:
42+
exclude:
43+
- "^docs:"
44+
- "^test:"
45+
- "^chore:"
46+
47+
release:
48+
replace_existing_artifacts: true
49+
50+
nfpms:
51+
- package_name: "sing-box-extensions"
52+
formats:
53+
- deb
54+
- rpm
55+
section: "default"
56+
maintainer: "Lantern Team <[email protected]>"
57+
description: |
58+
Sing Box Extensions
59+
vendor: "Brave New Software"
60+
homepage: "https://github.com/getlantern/sing-box-extensions"
61+
license: "GPL"
62+
contents:
63+
- src: cmd/sing-box-extensions/release/sing-box-extensions.service
64+
dst: /usr/lib/systemd/system/sing-box-extensions.service
65+
- src: cmd/sing-box-extensions/release/[email protected]
66+
dst: /usr/lib/systemd/system/[email protected]
67+
68+
furies:
69+
- account: getlantern
70+
formats:
71+
- deb
72+
- rpm
73+
74+
75+
dockers:
76+
- image_templates:
77+
- "getlantern/sing-box-extensions:latest"
78+
- "getlantern/sing-box-extensions:{{ .Tag }}"
79+
80+
dockerhub:
81+
- images:
82+
- getlantern/sing-box-extensions
83+
username: getlantern
84+
secret_name: DOCKER_PASSWORD
85+
full_description:
86+
from_file:
87+
path: ./README.md

Dockerfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM alpine:edge
2+
3+
# Set the timezone and install CA certificates
4+
RUN apk --no-cache add ca-certificates tzdata nftables
5+
6+
COPY sing-box-extensions /sing-box-extensions
7+
8+
# Set the entrypoint command
9+
ENTRYPOINT ["/sing-box-extensions", "-d", "/data", "-c", "/config.json", "run"]

cmd/sing-box-extensions/cmd_check.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import "fmt"
4+
5+
type CheckCmd struct {
6+
}
7+
8+
func check(configFile string) error {
9+
instance, cancel, err := prepare(configFile)
10+
11+
if err == nil {
12+
_ = instance.Close()
13+
}
14+
cancel()
15+
return err
16+
}
17+
18+
func (c *CheckCmd) Run() error {
19+
err := check(args.ConfigFile)
20+
if err == nil {
21+
fmt.Println("Configuration is valid")
22+
} else {
23+
return err
24+
}
25+
return nil
26+
}

cmd/sing-box-extensions/cmd_run.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
C "github.com/sagernet/sing-box/constant"
7+
"github.com/sagernet/sing-box/experimental/libbox"
8+
"github.com/sagernet/sing-box/log"
9+
E "github.com/sagernet/sing/common/exceptions"
10+
"os"
11+
"os/signal"
12+
"path/filepath"
13+
runtimeDebug "runtime/debug"
14+
"syscall"
15+
"time"
16+
)
17+
18+
type RunCmd struct {
19+
}
20+
21+
func prepare(configFile string) (*libbox.BoxService, context.CancelFunc, error) {
22+
ctx, cancel := context.WithCancel(newBaseContext())
23+
24+
var config string
25+
26+
if data, err := os.ReadFile(configFile); err != nil {
27+
return nil, cancel, fmt.Errorf("reading config file: %w", err)
28+
} else {
29+
config = string(data)
30+
}
31+
if err := libbox.Setup(&libbox.SetupOptions{
32+
BasePath: args.DataDir,
33+
WorkingPath: filepath.Join(args.DataDir, "data"),
34+
TempPath: filepath.Join(args.DataDir, "temp"),
35+
}); err != nil {
36+
return nil, cancel, fmt.Errorf("setup libbox: %w", err)
37+
}
38+
39+
instance, err := libbox.NewServiceWithContext(ctx, config, nil)
40+
return instance, cancel, err
41+
}
42+
43+
func create(config string) (*libbox.BoxService, context.CancelFunc, error) {
44+
instance, cancel, err := prepare(config)
45+
if err != nil {
46+
cancel()
47+
return nil, nil, fmt.Errorf("setup libbox: %w", err)
48+
}
49+
50+
osSignals := make(chan os.Signal, 1)
51+
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
52+
defer func() {
53+
signal.Stop(osSignals)
54+
close(osSignals)
55+
}()
56+
startCtx, finishStart := context.WithCancel(context.Background())
57+
go func() {
58+
_, loaded := <-osSignals
59+
if loaded {
60+
cancel()
61+
closeMonitor(startCtx)
62+
}
63+
}()
64+
err = instance.Start()
65+
finishStart()
66+
if err != nil {
67+
cancel()
68+
return nil, nil, E.Cause(err, "start service")
69+
}
70+
return instance, cancel, nil
71+
}
72+
73+
func closeMonitor(ctx context.Context) {
74+
time.Sleep(C.FatalStopTimeout)
75+
select {
76+
case <-ctx.Done():
77+
return
78+
default:
79+
}
80+
log.Fatal("sing-box did not close!")
81+
}
82+
83+
func (c *RunCmd) Run() error {
84+
osSignals := make(chan os.Signal, 1)
85+
signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
86+
defer signal.Stop(osSignals)
87+
for {
88+
instance, cancel, err := create(args.ConfigFile)
89+
if err != nil {
90+
return err
91+
}
92+
runtimeDebug.FreeOSMemory()
93+
for {
94+
osSignal := <-osSignals
95+
if osSignal == syscall.SIGHUP {
96+
err = check(args.ConfigFile)
97+
if err != nil {
98+
log.Error(E.Cause(err, "reload service"))
99+
continue
100+
}
101+
}
102+
cancel()
103+
closeCtx, closed := context.WithCancel(context.Background())
104+
go closeMonitor(closeCtx)
105+
err = instance.Close()
106+
closed()
107+
if osSignal != syscall.SIGHUP {
108+
if err != nil {
109+
log.Error(E.Cause(err, "sing-box did not closed properly"))
110+
}
111+
return nil
112+
}
113+
break
114+
}
115+
}
116+
}

cmd/sing-box-extensions/main.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"os"
8+
9+
"github.com/alexflint/go-arg"
10+
box "github.com/sagernet/sing-box"
11+
12+
"github.com/getlantern/sing-box-extensions/protocol"
13+
)
14+
15+
var args struct {
16+
DataDir string `arg:"-d,--data-dir" help:"data directory" default:"./data"`
17+
ConfigFile string `arg:"-c,--config" help:"Path to JSON config file" default:"./config.json"`
18+
19+
Run *RunCmd `arg:"subcommand:run" help:"start the server"`
20+
Check *CheckCmd `arg:"subcommand:check" help:"validate initial configuration"`
21+
}
22+
23+
func newBaseContext() context.Context {
24+
// Retrieve protocol registries
25+
inboundRegistry, outboundRegistry, endpointRegistry := protocol.GetRegistries()
26+
return box.Context(
27+
context.Background(),
28+
inboundRegistry,
29+
outboundRegistry,
30+
endpointRegistry,
31+
)
32+
}
33+
34+
func main() {
35+
var err error
36+
p := arg.MustParse(&args)
37+
switch {
38+
case args.Run != nil:
39+
err = args.Run.Run()
40+
case args.Check != nil:
41+
err = args.Check.Run()
42+
default:
43+
p.WriteHelp(os.Stderr)
44+
}
45+
if err != nil {
46+
if errors.Is(err, arg.ErrHelp) {
47+
_ = p.WriteHelpForSubcommand(os.Stderr, p.SubcommandNames()...)
48+
} else {
49+
_, _ = fmt.Fprintln(os.Stderr, err)
50+
}
51+
os.Exit(1)
52+
}
53+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[Unit]
2+
Description=sing-box-extensions service
3+
After=network.target nss-lookup.target network-online.target
4+
5+
[Service]
6+
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
7+
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
8+
ExecStart=/usr/bin/sing-box-extensions -d /var/lib/sing-box-extensions -c /etc/sing-box-extensions/config.json run
9+
ExecReload=/bin/kill -HUP $MAINPID
10+
Restart=on-failure
11+
RestartSec=10s
12+
LimitNOFILE=infinity
13+
14+
[Install]
15+
WantedBy=multi-user.target
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[Unit]
2+
Description=sing-box-extensions service
3+
After=network.target nss-lookup.target network-online.target
4+
5+
[Service]
6+
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
7+
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH
8+
ExecStart=/usr/bin/sing-box-extensions -d /var/lib/sing-box-extensions-%i -c /etc/sing-box-extensions/%i.json run
9+
ExecReload=/bin/kill -HUP $MAINPID
10+
Restart=on-failure
11+
RestartSec=10s
12+
LimitNOFILE=infinity
13+
14+
[Install]
15+
WantedBy=multi-user.target

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ replace github.com/tetratelabs/wazero => github.com/refraction-networking/wazero
1313
require (
1414
github.com/Jigsaw-Code/outline-sdk v0.0.19
1515
github.com/Jigsaw-Code/outline-sdk/x v0.0.2
16+
github.com/alexflint/go-arg v1.4.3
1617
github.com/getlantern/algeneva v0.0.0-20250307163401-1824e7b54f52
1718
github.com/getlantern/lantern-water v0.0.0-20250331153903-07abebe611e8
1819
github.com/gobwas/ws v1.4.0
@@ -33,6 +34,7 @@ require (
3334
github.com/ajg/form v1.5.1 // indirect
3435
github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 // indirect
3536
github.com/alecthomas/atomic v0.1.0-alpha2 // indirect
37+
github.com/alexflint/go-scalar v1.1.0 // indirect
3638
github.com/anacrolix/chansync v0.3.0 // indirect
3739
github.com/anacrolix/dht/v2 v2.19.2-0.20221121215055-066ad8494444 // indirect
3840
github.com/anacrolix/envpprof v1.3.0 // indirect

0 commit comments

Comments
 (0)