Skip to content

Commit

Permalink
Added config_example_source.yml and updated source.go to use it.
Browse files Browse the repository at this point in the history
  • Loading branch information
62w71st committed Dec 10, 2024
1 parent 99789c4 commit 3a4c60c
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 117 deletions.
42 changes: 42 additions & 0 deletions cmd/outline-ss-server/config_example_source.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2024 The Outline Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

services:
- listeners:
# TODO(sbruens): Allow a string-based listener config, as a convenient short-form
# to create a direct listener, e.g. `- tcp/[::]:9000`.
- type: tcp
address: "[::]:9000"
- type: udp
address: "[::]:9000"
# keys:
# - id: user-0
# cipher: chacha20-ietf-poly1305
# secret: Secret0
# - id: user-1
# cipher: chacha20-ietf-poly1305
# secret: Secret1
source:
url: "file://config_example.deprecated.yml"


- listeners:
- type: tcp
address: "[::]:9001"
- type: udp
address: "[::]:9001"
keys:
- id: user-2
cipher: chacha20-ietf-poly1305
secret: Secret2
116 changes: 4 additions & 112 deletions cmd/outline-ss-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@ import (
"net/http"
"os"
"os/signal"
"strconv"
"sync"
"syscall"
"time"

"github.com/Jigsaw-Code/outline-sdk/transport/shadowsocks"
"github.com/Jigsaw-Code/outline-ss-server/ipinfo"
"github.com/Jigsaw-Code/outline-ss-server/key"
outline_prometheus "github.com/Jigsaw-Code/outline-ss-server/prometheus"
"github.com/Jigsaw-Code/outline-ss-server/service"
"github.com/lmittmann/tint"
Expand Down Expand Up @@ -67,107 +65,6 @@ type OutlineServer struct {
replayCache service.ReplayCache
}

type CipherUpdater struct {
Ciphers service.CipherList
cond *sync.Cond
ciphersByID map[string]*service.CipherEntry
}

func (c *CipherUpdater) AddKey(key key.Key) error {
// Wait until Ciphers are initialized.
c.cond.L.Lock()
for c.Ciphers == nil {
c.cond.Wait()
}
c.cond.L.Unlock()
cryptoKey, err := shadowsocks.NewEncryptionKey(key.Cipher, key.Secret)
if err != nil {
return fmt.Errorf("failed to create encyption key for key %v: %w", key.ID, err)
}
entry := service.MakeCipherEntry(key.ID, cryptoKey, key.Secret)
slog.Info("Added key ", "keyID", key.ID)
c.Ciphers.AddEntry(&entry)
// Store the entry in a map for fast removal
c.ciphersByID[key.ID] = &entry
return nil
}

func (c *CipherUpdater) RemoveKey(key key.Key) error {
if c.Ciphers == nil {
return fmt.Errorf("no Cipher available while removing key %v", key.ID)
}
entry, exists := c.ciphersByID[key.ID]
if exists {
c.Ciphers.RemoveEntry(entry)
return nil
} else {
return fmt.Errorf("key %v was not found", key.ID)
}
}

func (c *CipherUpdater) AddCipher(ciphers service.CipherList) {
c.cond.L.Lock()
if c.Ciphers == nil {
c.Ciphers = ciphers
c.cond.Broadcast() // Notify all waiting goroutines
}
c.cond.L.Unlock()
}

func (s *OutlineServer) loadSource(filename string) error {
file_source := key.NewFileSource(filename)
var config *Config
var wg sync.WaitGroup
wg.Add(1)
updater := &CipherUpdater{
cond: sync.NewCond(&sync.Mutex{}),
ciphersByID: make(map[string]*service.CipherEntry),
}
go func() {
for cmd := range file_source.Channel() {
switch cmd.Action {
case key.AddAction:
if config == nil {
config = &Config{
Services: []ServiceConfig{
ServiceConfig{
Listeners: []ListenerConfig{
ListenerConfig{Type: listenerTypeTCP, Address: "[::]:" + strconv.Itoa(cmd.Key.Port)},
ListenerConfig{Type: listenerTypeUDP, Address: "[::]:" + strconv.Itoa(cmd.Key.Port)},
},
Keys: []KeyConfig{
KeyConfig{cmd.Key.ID, cmd.Key.Cipher, cmd.Key.Secret},
},
},
},
}
wg.Done()
} else {
updater.AddKey(cmd.Key)
}
case key.RemoveAction:
updater.RemoveKey(cmd.Key)
}
}
}()
// Wait until at least we have one key in the config.
wg.Wait()

// We hot swap the config by having the old and new listeners both live at
// the same time. This means we create listeners for the new config first,
// and then close the old ones after.
stopConfig, err := s.runConfig(*config, updater)
if err != nil {
return err
}

if err := s.Stop(); err != nil {
slog.Warn("Failed to stop old config.", "err", err)
}
s.stopConfig = stopConfig
return nil
}

func (s *OutlineServer) loadConfig(filename string) error {
configData, err := os.ReadFile(filename)
if err != nil {
Expand All @@ -184,7 +81,7 @@ func (s *OutlineServer) loadConfig(filename string) error {
// We hot swap the config by having the old and new listeners both live at
// the same time. This means we create listeners for the new config first,
// and then close the old ones after.
stopConfig, err := s.runConfig(*config, nil)
stopConfig, err := s.runConfig(*config)
if err != nil {
return err
}
Expand Down Expand Up @@ -219,7 +116,8 @@ func newCipherListFromConfig(config ServiceConfig) (service.CipherList, error) {
ciphers := service.NewCipherList()
ciphers.Update(cipherList)

config.Source.Register(ciphers)
slog.Info("newCipherListFromConfig with config", "config", config)
config.Source.Register(ciphers, slog.Default())

return ciphers, nil
}
Expand Down Expand Up @@ -285,7 +183,7 @@ func (ls *listenerSet) Len() int {
return len(ls.listenerCloseFuncs)
}

func (s *OutlineServer) runConfig(config Config, updater *CipherUpdater) (func() error, error) {
func (s *OutlineServer) runConfig(config Config) (func() error, error) {
startErrCh := make(chan error)
stopErrCh := make(chan error)
stopCh := make(chan struct{})
Expand Down Expand Up @@ -322,9 +220,6 @@ func (s *OutlineServer) runConfig(config Config, updater *CipherUpdater) (func()
addr := fmt.Sprintf(":%d", portNum)

ciphers := service.NewCipherList()
if updater != nil {
updater.AddCipher(ciphers)
}
ciphers.Update(cipherList)
ssService, err := service.NewShadowsocksService(
service.WithCiphers(ciphers),
Expand All @@ -351,9 +246,6 @@ func (s *OutlineServer) runConfig(config Config, updater *CipherUpdater) (func()
}
for _, serviceConfig := range config.Services {
ciphers, err := newCipherListFromConfig(serviceConfig)
if updater != nil {
updater.AddCipher(ciphers)
}
if err != nil {
return fmt.Errorf("failed to create cipher list from config: %v", err)
}
Expand Down
15 changes: 15 additions & 0 deletions cmd/outline-ss-server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,18 @@ func TestRunOutlineServer(t *testing.T) {
t.Errorf("Error while stopping server: %v", err)
}
}

func TestRunOutlineServerWithSource(t *testing.T) {
serverMetrics := newPrometheusServerMetrics()
serviceMetrics, err := prometheus.NewServiceMetrics(nil)
if err != nil {
t.Fatalf("Failed to create Prometheus service metrics: %v", err)
}
server, err := RunOutlineServer("config_example_source.yml", 30*time.Second, serverMetrics, serviceMetrics, 10000)
if err != nil {
t.Fatalf("RunOutlineServer() error = %v", err)
}
if err := server.Stop(); err != nil {
t.Errorf("Error while stopping server: %v", err)
}
}
13 changes: 8 additions & 5 deletions cmd/outline-ss-server/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import (
)

type Source struct {
Url string // URL of the key source, e.g. "file://path/to/keys"
updater KeyUpdater
Url string // URL of the key source, e.g. "file://path/to/keys"
}

type KeyUpdater struct {
Expand Down Expand Up @@ -46,19 +45,21 @@ func (c *KeyUpdater) RemoveKey(key key.Key) error {
}
}

func (s *Source) Register(c service.CipherList) {
func (s *Source) Register(c service.CipherList, logger *slog.Logger) {
if strings.HasPrefix(s.Url, "file://") {
newFileUpdater(c, key.NewFileSource(s.Url[7:])).Listen()
newFileUpdater(c, key.NewFileSource(s.Url[7:]), logger).Listen()
}
}

type FileUpdater struct {
KeyUpdater
fileSource key.Source
logger *slog.Logger
}

func newFileUpdater(c service.CipherList, s key.Source) *FileUpdater {
func newFileUpdater(c service.CipherList, s key.Source, logger *slog.Logger) *FileUpdater {
fu := &FileUpdater{
logger: logger,
fileSource: s,
}
fu.KeyUpdater.Ciphers = c
Expand All @@ -72,8 +73,10 @@ func (fu *FileUpdater) Listen() {
switch cmd.Action {
case key.AddAction:
fu.KeyUpdater.AddKey(cmd.Key)
fu.logger.Info("Added key ", "keyID", cmd.Key.ID)
case key.RemoveAction:
fu.KeyUpdater.RemoveKey(cmd.Key)
fu.logger.Info("Removed key ", "keyID", cmd.Key.ID)
}
}
}()
Expand Down

0 comments on commit 3a4c60c

Please sign in to comment.