Checkpoint
This commit is contained in:
206
cmd/styx/config.go
Normal file
206
cmd/styx/config.go
Normal file
@@ -0,0 +1,206 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/hashicorp/hcl/v2/gohcl"
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
|
||||
"git.maze.io/maze/styx/dataset"
|
||||
"git.maze.io/maze/styx/internal/cryptutil"
|
||||
"git.maze.io/maze/styx/logger"
|
||||
"git.maze.io/maze/styx/policy"
|
||||
"git.maze.io/maze/styx/proxy"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Proxy ProxyConfig `hcl:"proxy,block"`
|
||||
Policy []PolicyConfig `hcl:"policy,block"`
|
||||
Data DataConfig `hcl:"data,block"`
|
||||
}
|
||||
|
||||
func (c Config) Proxies(log logger.Structured) ([]*proxy.Proxy, error) {
|
||||
policies := make(map[string]*policy.Policy)
|
||||
for _, pc := range c.Policy {
|
||||
p, err := policy.New(pc.Path, pc.Package)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("policy %s: %w", pc.Name, err)
|
||||
}
|
||||
policies[pc.Name] = p
|
||||
}
|
||||
|
||||
var (
|
||||
onRequest []proxy.RequestHandler
|
||||
onResponse []proxy.ResponseHandler
|
||||
)
|
||||
for _, name := range c.Proxy.On.Request {
|
||||
log.Value("policy", name).Debug("Resolving request policy")
|
||||
p, ok := policies[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("on request: no policy named %q", name)
|
||||
}
|
||||
onRequest = append(onRequest, policy.NewRequestHandler(p))
|
||||
}
|
||||
for _, name := range c.Proxy.On.Response {
|
||||
log.Value("policy", name).Debug("Resolving response policy")
|
||||
p, ok := policies[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("on response: no policy named %q", name)
|
||||
}
|
||||
onResponse = append(onResponse, policy.NewResponseHandler(p))
|
||||
}
|
||||
|
||||
var proxies []*proxy.Proxy
|
||||
for _, pc := range c.Proxy.Port {
|
||||
log.Value("port", pc.Listen).Debug("Configuring proxy port")
|
||||
p, err := pc.Proxy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.OnRequest = append(p.OnRequest, onRequest...)
|
||||
p.OnResponse = append(p.OnResponse, onResponse...)
|
||||
proxies = append(proxies, p)
|
||||
}
|
||||
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
type ProxyConfig struct {
|
||||
Port []PortConfig `hcl:"port,block"`
|
||||
Upstream []string `hcl:"upstream"`
|
||||
On ProxyPolicyConfig `hcl:"on,block"`
|
||||
}
|
||||
|
||||
type PortConfig struct {
|
||||
Listen string `hcl:"port,label"`
|
||||
TLS *PortTLSConfig `hcl:"tls,block"`
|
||||
Transparent int `hcl:"transparent,optional"`
|
||||
Name string `hcl:"name,optional"`
|
||||
}
|
||||
|
||||
type PortTLSConfig struct {
|
||||
Cert string `hcl:"cert"`
|
||||
Key string `hcl:"key,optional"`
|
||||
CA string `hcl:"ca,optional"`
|
||||
}
|
||||
|
||||
func (c PortConfig) Proxy() (*proxy.Proxy, error) {
|
||||
p := proxy.New()
|
||||
if c.Transparent > 0 {
|
||||
p.OnConnect = append(p.OnConnect, proxy.Transparent(c.Transparent))
|
||||
} else if c.TLS != nil {
|
||||
cert, err := cryptutil.LoadTLSCertificate(c.TLS.Cert, c.TLS.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := new(tls.Config)
|
||||
config.Certificates = []tls.Certificate{cert}
|
||||
if c.TLS.CA != "" {
|
||||
roots, err := cryptutil.LoadRoots(c.TLS.CA)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.RootCAs = roots
|
||||
}
|
||||
|
||||
p.OnConnect = append(p.OnConnect, proxy.TLS(config))
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type ProxyPolicyConfig struct {
|
||||
Intercept []string `hcl:"intercept,optional"`
|
||||
Request []string `hcl:"request,optional"`
|
||||
Response []string `hcl:"response,optional"`
|
||||
}
|
||||
|
||||
type PolicyConfig struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Path string `hcl:"path"`
|
||||
Package string `hcl:"package,optional"`
|
||||
}
|
||||
|
||||
type DataConfig struct {
|
||||
Path string `hcl:"path,optional"`
|
||||
Domains []DomainDataConfig `hcl:"domain,block"`
|
||||
Networks []NetworkDataConfig `hcl:"network,block"`
|
||||
}
|
||||
|
||||
func (c DataConfig) Configure() error {
|
||||
for _, dc := range c.Domains {
|
||||
if err := dc.Configure(); err != nil {
|
||||
return fmt.Errorf("error setting up domain data %q: %w", dc.Name, err)
|
||||
}
|
||||
}
|
||||
for _, nc := range c.Networks {
|
||||
if err := nc.Configure(); err != nil {
|
||||
return fmt.Errorf("error setting up network data %q: %w", nc.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DomainDataConfig struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Type string `hcl:"type"`
|
||||
Body hcl.Body `hcl:",remain"`
|
||||
}
|
||||
|
||||
func (c DomainDataConfig) Configure() error {
|
||||
switch c.Type {
|
||||
case "", "list":
|
||||
var justTheList struct {
|
||||
List []string `hcl:"list"`
|
||||
}
|
||||
if diag := gohcl.DecodeBody(c.Body, nil, &justTheList); diag.HasErrors() {
|
||||
return diag
|
||||
}
|
||||
|
||||
dataset.Domains[c.Name] = dataset.NewDomainList(justTheList.List...)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown type %q", c.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type NetworkDataConfig struct {
|
||||
Name string `hcl:"name,label"`
|
||||
Type string `hcl:"type"`
|
||||
Body hcl.Body `hcl:",remain"`
|
||||
}
|
||||
|
||||
func (c NetworkDataConfig) Configure() error {
|
||||
switch c.Type {
|
||||
case "", "list":
|
||||
var justTheList struct {
|
||||
List []string `hcl:"list"`
|
||||
}
|
||||
if diag := gohcl.DecodeBody(c.Body, nil, &justTheList); diag.HasErrors() {
|
||||
return diag
|
||||
}
|
||||
|
||||
list, err := dataset.NewNetworkTree(justTheList.List...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataset.Networks[c.Name] = list
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown type %q", c.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Load(name string) (*Config, error) {
|
||||
config := new(Config)
|
||||
if err := hclsimple.DecodeFile(name, nil, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return config, nil
|
||||
}
|
120
cmd/styx/main.go
120
cmd/styx/main.go
@@ -2,84 +2,90 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||
|
||||
"git.maze.io/maze/styx/internal/log"
|
||||
"git.maze.io/maze/styx/logger"
|
||||
"git.maze.io/maze/styx/proxy"
|
||||
"git.maze.io/maze/styx/proxy/cache"
|
||||
"git.maze.io/maze/styx/proxy/match"
|
||||
"git.maze.io/maze/styx/proxy/mitm"
|
||||
"git.maze.io/maze/styx/proxy/resolver"
|
||||
)
|
||||
|
||||
func main() {
|
||||
configFlag := flag.String("config", "styx.hcl", "Configuration file")
|
||||
traceFlag := flag.Bool("T", false, "Enable trace level logging")
|
||||
debugFlag := flag.Bool("D", false, "Enable debug level logging")
|
||||
var (
|
||||
configFile = "styx.hcl"
|
||||
traceFlag bool
|
||||
debugFlag bool
|
||||
)
|
||||
|
||||
flag.StringVar(&configFile, "config", configFile, "configuration file")
|
||||
flag.BoolVar(&traceFlag, "T", traceFlag, "enable trace logging")
|
||||
flag.BoolVar(&debugFlag, "D", debugFlag, "enable debug logging")
|
||||
flag.Parse()
|
||||
|
||||
if *traceFlag {
|
||||
log.SetLevel(log.TraceLevel)
|
||||
} else if *debugFlag {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
log := logger.StandardLog.Value("path", configFile)
|
||||
|
||||
if traceFlag {
|
||||
log.SetLevel(logger.TraceLevel)
|
||||
} else if debugFlag {
|
||||
log.SetLevel(logger.DebugLevel)
|
||||
}
|
||||
|
||||
config, err := load(*configFlag)
|
||||
config, err := Load(configFile)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Err(err).Fatal("Invalid configuration file")
|
||||
}
|
||||
|
||||
matchers, err := config.Match.Matchers()
|
||||
if err = config.Data.Configure(); err != nil {
|
||||
log.Err(err).Fatal("Invalid data configuration")
|
||||
}
|
||||
|
||||
proxies, err := config.Proxies(log)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
} else if err = config.Proxy.Policy.Configure(matchers); err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Err(err).Fatal("Error configuring proxy ports")
|
||||
}
|
||||
|
||||
var ca mitm.Authority
|
||||
if config.MITM != nil {
|
||||
if ca, err = mitm.New(config.MITM); err != nil {
|
||||
log.Fatal().Err(err).Msg("error configuring mitm")
|
||||
var (
|
||||
errs = make(chan error, 1)
|
||||
done = make(chan struct{}, 1)
|
||||
sigs = make(chan os.Signal, 1)
|
||||
)
|
||||
|
||||
for i, p := range proxies {
|
||||
go run(config.Proxy.Port[i].Listen, p, errs)
|
||||
}
|
||||
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGHUP)
|
||||
|
||||
for {
|
||||
select {
|
||||
case sig := <-sigs:
|
||||
switch sig {
|
||||
case syscall.SIGHUP:
|
||||
log.Value("signal", sig.String()).Warn("Ignored reload signal ¯\\_(ツ)_/¯")
|
||||
default:
|
||||
log.Value("signal", sig.String()).Info("Shutting down on signal")
|
||||
return
|
||||
}
|
||||
|
||||
case <-done:
|
||||
log.Info("Shutting down gracefully")
|
||||
return
|
||||
|
||||
case err = <-errs:
|
||||
log.Err(err).Fatal("Shutting down because of fatal error in proxy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server, err := proxy.New(&config.Proxy, ca)
|
||||
func run(addr string, port *proxy.Proxy, errors chan<- error) {
|
||||
log := logger.StandardLog.Value("port", addr)
|
||||
log.Info("Proxy port starting")
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
errors <- err
|
||||
return
|
||||
}
|
||||
|
||||
if err = server.Start(); err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
signalChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-signalChannel
|
||||
|
||||
server.Close()
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
DNS *resolver.Config `hcl:"dns,block"`
|
||||
Proxy proxy.Config `hcl:"proxy,block"`
|
||||
MITM *mitm.Config `hcl:"mitm,block"`
|
||||
Cache *cache.Config `hcl:"cache,block"`
|
||||
Match *match.Config `hcl:"match,block"`
|
||||
}
|
||||
|
||||
func load(name string) (*Config, error) {
|
||||
config := new(Config)
|
||||
if err := hclsimple.DecodeFile(name, nil, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if config.DNS != nil {
|
||||
config.Proxy.Resolver = resolver.New(*config.DNS)
|
||||
}
|
||||
|
||||
return config, nil
|
||||
log.Info("Proxy port ready")
|
||||
errors <- port.Serve(l)
|
||||
}
|
||||
|
Reference in New Issue
Block a user