mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-04 01:09:36 +02:00
allow pomerium to be embedded as a library (#3415)
This commit is contained in:
parent
6e1ebffc59
commit
d1037d784a
27 changed files with 292 additions and 207 deletions
227
pkg/cmd/pomerium/pomerium.go
Normal file
227
pkg/cmd/pomerium/pomerium.go
Normal file
|
@ -0,0 +1,227 @@
|
|||
// Package pomerium houses the main pomerium CLI command.
|
||||
//
|
||||
package pomerium
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/pomerium/pomerium/authenticate"
|
||||
"github.com/pomerium/pomerium/authorize"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
databroker_service "github.com/pomerium/pomerium/databroker"
|
||||
"github.com/pomerium/pomerium/internal/autocert"
|
||||
"github.com/pomerium/pomerium/internal/controlplane"
|
||||
"github.com/pomerium/pomerium/internal/databroker"
|
||||
"github.com/pomerium/pomerium/internal/events"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/registry"
|
||||
"github.com/pomerium/pomerium/internal/version"
|
||||
"github.com/pomerium/pomerium/pkg/envoy"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
"github.com/pomerium/pomerium/proxy"
|
||||
)
|
||||
|
||||
// Run runs the main pomerium application.
|
||||
func Run(ctx context.Context, src config.Source) error {
|
||||
log.Info(ctx).
|
||||
Str("envoy_version", files.FullVersion()).
|
||||
Str("version", version.FullVersion()).
|
||||
Msg("cmd/pomerium")
|
||||
|
||||
src = databroker.NewConfigSource(ctx, src)
|
||||
logMgr := config.NewLogManager(ctx, src)
|
||||
defer logMgr.Close()
|
||||
|
||||
// trigger changes when underlying files are changed
|
||||
src = config.NewFileWatcherSource(src)
|
||||
|
||||
src, err := autocert.New(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// override the default http transport so we can use the custom CA in the TLS client config (#1570)
|
||||
http.DefaultTransport = config.NewHTTPTransport(src)
|
||||
|
||||
metricsMgr := config.NewMetricsManager(ctx, src)
|
||||
defer metricsMgr.Close()
|
||||
traceMgr := config.NewTraceManager(ctx, src)
|
||||
defer traceMgr.Close()
|
||||
|
||||
eventsMgr := events.New()
|
||||
|
||||
// setup the control plane
|
||||
controlPlane, err := controlplane.NewServer(src.GetConfig(), metricsMgr, eventsMgr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating control plane: %w", err)
|
||||
}
|
||||
src.OnConfigChange(ctx,
|
||||
func(ctx context.Context, cfg *config.Config) {
|
||||
if err := controlPlane.OnConfigChange(ctx, cfg); err != nil {
|
||||
log.Error(ctx).Err(err).Msg("config change")
|
||||
}
|
||||
})
|
||||
|
||||
if err = controlPlane.OnConfigChange(ctx, src.GetConfig()); err != nil {
|
||||
return fmt.Errorf("applying config: %w", err)
|
||||
}
|
||||
|
||||
log.Info(ctx).
|
||||
Str("grpc-port", src.GetConfig().GRPCPort).
|
||||
Str("http-port", src.GetConfig().HTTPPort).
|
||||
Str("outbound-port", src.GetConfig().OutboundPort).
|
||||
Str("metrics-port", src.GetConfig().MetricsPort).
|
||||
Str("debug-port", src.GetConfig().DebugPort).
|
||||
Msg("server started")
|
||||
|
||||
// create envoy server
|
||||
envoyServer, err := envoy.NewServer(ctx, src, controlPlane.Builder)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating envoy server: %w", err)
|
||||
}
|
||||
defer envoyServer.Close()
|
||||
|
||||
// add services
|
||||
if err := setupAuthenticate(ctx, src, controlPlane); err != nil {
|
||||
return err
|
||||
}
|
||||
var authorizeServer *authorize.Authorize
|
||||
if config.IsAuthorize(src.GetConfig().Options.Services) {
|
||||
authorizeServer, err = setupAuthorize(ctx, src, controlPlane)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var dataBrokerServer *databroker_service.DataBroker
|
||||
if config.IsDataBroker(src.GetConfig().Options.Services) {
|
||||
dataBrokerServer, err = setupDataBroker(ctx, src, controlPlane, eventsMgr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up databroker: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = setupRegistryReporter(ctx, src); err != nil {
|
||||
return fmt.Errorf("setting up registry reporter: %w", err)
|
||||
}
|
||||
if err := setupProxy(ctx, src, controlPlane); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
go func(ctx context.Context) {
|
||||
ch := make(chan os.Signal, 2)
|
||||
defer signal.Stop(ch)
|
||||
|
||||
signal.Notify(ch, os.Interrupt)
|
||||
signal.Notify(ch, syscall.SIGTERM)
|
||||
|
||||
select {
|
||||
case <-ch:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
cancel()
|
||||
}(ctx)
|
||||
|
||||
// run everything
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
if authorizeServer != nil {
|
||||
eg.Go(func() error {
|
||||
return authorizeServer.Run(ctx)
|
||||
})
|
||||
}
|
||||
eg.Go(func() error {
|
||||
return controlPlane.Run(ctx)
|
||||
})
|
||||
if dataBrokerServer != nil {
|
||||
eg.Go(func() error {
|
||||
return dataBrokerServer.Run(ctx)
|
||||
})
|
||||
}
|
||||
return eg.Wait()
|
||||
}
|
||||
|
||||
func setupAuthenticate(ctx context.Context, src config.Source, controlPlane *controlplane.Server) error {
|
||||
if !config.IsAuthenticate(src.GetConfig().Options.Services) {
|
||||
return nil
|
||||
}
|
||||
|
||||
svc, err := authenticate.New(src.GetConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating authenticate service: %w", err)
|
||||
}
|
||||
err = controlPlane.EnableAuthenticate(svc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding authenticate service to control plane: %w", err)
|
||||
}
|
||||
|
||||
src.OnConfigChange(ctx, svc.OnConfigChange)
|
||||
svc.OnConfigChange(ctx, src.GetConfig())
|
||||
log.Info(ctx).Msg("enabled authenticate service")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupAuthorize(ctx context.Context, src config.Source, controlPlane *controlplane.Server) (*authorize.Authorize, error) {
|
||||
svc, err := authorize.New(src.GetConfig())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating authorize service: %w", err)
|
||||
}
|
||||
envoy_service_auth_v3.RegisterAuthorizationServer(controlPlane.GRPCServer, svc)
|
||||
|
||||
log.Info(context.TODO()).Msg("enabled authorize service")
|
||||
src.OnConfigChange(ctx, svc.OnConfigChange)
|
||||
svc.OnConfigChange(ctx, src.GetConfig())
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func setupDataBroker(ctx context.Context,
|
||||
src config.Source,
|
||||
controlPlane *controlplane.Server,
|
||||
eventsMgr *events.Manager,
|
||||
) (*databroker_service.DataBroker, error) {
|
||||
svc, err := databroker_service.New(src.GetConfig(), eventsMgr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating databroker service: %w", err)
|
||||
}
|
||||
svc.Register(controlPlane.GRPCServer)
|
||||
log.Info(context.TODO()).Msg("enabled databroker service")
|
||||
src.OnConfigChange(ctx, svc.OnConfigChange)
|
||||
svc.OnConfigChange(ctx, src.GetConfig())
|
||||
return svc, nil
|
||||
}
|
||||
|
||||
func setupRegistryReporter(ctx context.Context, src config.Source) error {
|
||||
reporter := registry.NewReporter()
|
||||
src.OnConfigChange(ctx, reporter.OnConfigChange)
|
||||
reporter.OnConfigChange(ctx, src.GetConfig())
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupProxy(ctx context.Context, src config.Source, controlPlane *controlplane.Server) error {
|
||||
if !config.IsProxy(src.GetConfig().Options.Services) {
|
||||
return nil
|
||||
}
|
||||
|
||||
svc, err := proxy.New(src.GetConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating proxy service: %w", err)
|
||||
}
|
||||
err = controlPlane.EnableProxy(svc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error adding proxy service to control plane: %w", err)
|
||||
}
|
||||
|
||||
log.Info(context.TODO()).Msg("enabled proxy service")
|
||||
src.OnConfigChange(ctx, svc.OnConfigChange)
|
||||
svc.OnConfigChange(ctx, src.GetConfig())
|
||||
|
||||
return nil
|
||||
}
|
148
pkg/cmd/pomerium/pomerium_test.go
Normal file
148
pkg/cmd/pomerium/pomerium_test.go
Normal file
|
@ -0,0 +1,148 @@
|
|||
package pomerium_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/pkg/cmd/pomerium"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
)
|
||||
|
||||
func Test_run(t *testing.T) {
|
||||
os.Clearenv()
|
||||
|
||||
run := func(ctx context.Context, configFile string) error {
|
||||
src, err := config.NewFileOrEnvironmentSource(configFile, files.FullVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pomerium.Run(ctx, src)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
configFileFlag string
|
||||
check func(require.TestingT, error, ...any)
|
||||
}{
|
||||
{"nil configuration", "", require.Error},
|
||||
{"bad proxy no authenticate url", `
|
||||
{
|
||||
"address": ":9433",
|
||||
"grpc_address": ":9444",
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "proxy",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"bad authenticate no cookie secret", `
|
||||
{
|
||||
"address": ":9433",
|
||||
"grpc_address": ":9444",
|
||||
"insecure_server": true,
|
||||
"authenticate_service_url": "https://authenticate.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "authenticate",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"bad authorize service bad shared key", `
|
||||
{
|
||||
"address": ":9433",
|
||||
"grpc_address": ":9444",
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"shared_secret": "^^^",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "authorize",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"bad http port", `
|
||||
{
|
||||
"address": ":-1",
|
||||
"grpc_address": ":9444",
|
||||
"grpc_insecure": true,
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"authenticate_service_url": "https://authenticate.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "proxy",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"bad redirect port", `
|
||||
{
|
||||
"address": ":9433",
|
||||
"http_redirect_addr":":-1",
|
||||
"grpc_address": ":9444",
|
||||
"grpc_insecure": true,
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"authenticate_service_url": "https://authenticate.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "proxy",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"bad metrics port ", `
|
||||
{
|
||||
"address": ":9433",
|
||||
"metrics_address": ":-1",
|
||||
"grpc_insecure": true,
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"authenticate_service_url": "https://authenticate.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "proxy",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
{"malformed tracing provider", `
|
||||
{
|
||||
"tracing_provider": "bad tracing provider",
|
||||
"address": ":9433",
|
||||
"grpc_address": ":9444",
|
||||
"grpc_insecure": true,
|
||||
"insecure_server": true,
|
||||
"authorize_service_url": "https://authorize.corp.example",
|
||||
"authenticate_service_url": "https://authenticate.corp.example",
|
||||
"shared_secret": "YixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"cookie_secret": "zixWi1MYh77NMECGGIJQevoonYtVF+ZPRkQZrrmeRqM=",
|
||||
"services": "proxy",
|
||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||
}
|
||||
`, require.Error},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tmpFile, err := os.CreateTemp(os.TempDir(), "*.json")
|
||||
if err != nil {
|
||||
t.Fatal("Cannot create temporary file", err)
|
||||
}
|
||||
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||
fn := tmpFile.Name()
|
||||
if _, err := tmpFile.Write([]byte(tt.configFileFlag)); err != nil {
|
||||
tmpFile.Close()
|
||||
t.Fatal(err)
|
||||
}
|
||||
configFile := fn
|
||||
|
||||
ctx, clearTimeout := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer clearTimeout()
|
||||
|
||||
tt.check(t, run(ctx, configFile))
|
||||
})
|
||||
}
|
||||
}
|
355
pkg/envoy/envoy.go
Normal file
355
pkg/envoy/envoy.go
Normal file
|
@ -0,0 +1,355 @@
|
|||
// Package envoy creates and configures an envoy server.
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
envoy_config_bootstrap_v3 "github.com/envoyproxy/go-control-plane/envoy/config/bootstrap/v3"
|
||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/natefinch/atomic"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/config/envoyconfig"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
)
|
||||
|
||||
const (
|
||||
configFileName = "envoy-config.yaml"
|
||||
)
|
||||
|
||||
type serverOptions struct {
|
||||
services string
|
||||
logLevel string
|
||||
}
|
||||
|
||||
// A Server is a pomerium proxy implemented via envoy.
|
||||
type Server struct {
|
||||
wd string
|
||||
cmd *exec.Cmd
|
||||
|
||||
builder *envoyconfig.Builder
|
||||
grpcPort, httpPort string
|
||||
envoyPath string
|
||||
|
||||
monitorProcessCancel context.CancelFunc
|
||||
|
||||
mu sync.Mutex
|
||||
options serverOptions
|
||||
}
|
||||
|
||||
// NewServer creates a new server with traffic routed by envoy.
|
||||
func NewServer(ctx context.Context, src config.Source, builder *envoyconfig.Builder) (*Server, error) {
|
||||
envoyPath, err := Extract()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("extracting envoy: %w", err)
|
||||
}
|
||||
|
||||
srv := &Server{
|
||||
wd: path.Dir(envoyPath),
|
||||
builder: builder,
|
||||
grpcPort: src.GetConfig().GRPCPort,
|
||||
httpPort: src.GetConfig().HTTPPort,
|
||||
envoyPath: envoyPath,
|
||||
|
||||
monitorProcessCancel: func() {},
|
||||
}
|
||||
go srv.runProcessCollector(ctx)
|
||||
|
||||
src.OnConfigChange(ctx, srv.onConfigChange)
|
||||
srv.onConfigChange(ctx, src.GetConfig())
|
||||
|
||||
log.Info(ctx).
|
||||
Str("path", envoyPath).
|
||||
Str("checksum", files.Checksum()).
|
||||
Msg("running envoy")
|
||||
|
||||
return srv, nil
|
||||
}
|
||||
|
||||
// Close kills any underlying envoy process.
|
||||
func (srv *Server) Close() error {
|
||||
srv.monitorProcessCancel()
|
||||
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
var err error
|
||||
if srv.cmd != nil && srv.cmd.Process != nil {
|
||||
err = srv.cmd.Process.Kill()
|
||||
if err != nil {
|
||||
log.Error(context.TODO()).Err(err).Str("service", "envoy").Msg("envoy: failed to kill process on close")
|
||||
}
|
||||
srv.cmd = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (srv *Server) onConfigChange(ctx context.Context, cfg *config.Config) {
|
||||
srv.update(ctx, cfg)
|
||||
}
|
||||
|
||||
func (srv *Server) update(ctx context.Context, cfg *config.Config) {
|
||||
srv.mu.Lock()
|
||||
defer srv.mu.Unlock()
|
||||
|
||||
options := serverOptions{
|
||||
services: cfg.Options.Services,
|
||||
logLevel: firstNonEmpty(cfg.Options.ProxyLogLevel, cfg.Options.LogLevel, "debug"),
|
||||
}
|
||||
|
||||
if cmp.Equal(srv.options, options, cmp.AllowUnexported(serverOptions{})) {
|
||||
log.Debug(ctx).Str("service", "envoy").Msg("envoy: no config changes detected")
|
||||
return
|
||||
}
|
||||
srv.options = options
|
||||
|
||||
log.Info(ctx).Msg("envoy: starting envoy process")
|
||||
if err := srv.run(ctx, cfg); err != nil {
|
||||
log.Error(ctx).Err(err).Str("service", "envoy").Msg("envoy: failed to run envoy process")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) run(ctx context.Context, cfg *config.Config) error {
|
||||
// cancel any process monitor since we will be killing the previous process
|
||||
srv.monitorProcessCancel()
|
||||
|
||||
if err := srv.writeConfig(ctx, cfg); err != nil {
|
||||
log.Error(ctx).Err(err).Str("service", "envoy").Msg("envoy: failed to write envoy config")
|
||||
return err
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-c", configFileName,
|
||||
"--log-level", srv.options.logLevel,
|
||||
"--log-format", "[LOG_FORMAT]%l--%n--%v",
|
||||
"--log-format-escaped",
|
||||
}
|
||||
|
||||
exePath, args := srv.prepareRunEnvoyCommand(ctx, args)
|
||||
cmd := exec.Command(exePath, args...)
|
||||
cmd.Dir = srv.wd
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating stderr pipe for envoy: %w", err)
|
||||
}
|
||||
go srv.handleLogs(ctx, stderr)
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating stdout pipe for envoy: %w", err)
|
||||
}
|
||||
go srv.handleLogs(ctx, stdout)
|
||||
|
||||
// make sure envoy is killed if we're killed
|
||||
cmd.SysProcAttr = sysProcAttr
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting envoy: %w", err)
|
||||
}
|
||||
// call Wait to avoid zombie processes
|
||||
go func() { _ = cmd.Wait() }()
|
||||
|
||||
// monitor the process so we exit if it prematurely exits
|
||||
var monitorProcessCtx context.Context
|
||||
monitorProcessCtx, srv.monitorProcessCancel = context.WithCancel(context.Background())
|
||||
go srv.monitorProcess(monitorProcessCtx, int32(cmd.Process.Pid))
|
||||
|
||||
srv.cmd = cmd
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (srv *Server) writeConfig(ctx context.Context, cfg *config.Config) error {
|
||||
confBytes, err := srv.buildBootstrapConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfgPath := filepath.Join(srv.wd, configFileName)
|
||||
log.Debug(ctx).Str("service", "envoy").Str("location", cfgPath).Msg("wrote config file to location")
|
||||
|
||||
return atomic.WriteFile(cfgPath, bytes.NewReader(confBytes))
|
||||
}
|
||||
|
||||
func (srv *Server) buildBootstrapConfig(cfg *config.Config) ([]byte, error) {
|
||||
nodeCfg := &envoy_config_core_v3.Node{
|
||||
Id: telemetry.ServiceName(cfg.Options.Services),
|
||||
Cluster: telemetry.ServiceName(cfg.Options.Services),
|
||||
}
|
||||
|
||||
adminCfg, err := srv.builder.BuildBootstrapAdmin(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dynamicCfg := &envoy_config_bootstrap_v3.Bootstrap_DynamicResources{
|
||||
AdsConfig: &envoy_config_core_v3.ApiConfigSource{
|
||||
ApiType: envoy_config_core_v3.ApiConfigSource_ApiType(envoy_config_core_v3.ApiConfigSource_ApiType_value["DELTA_GRPC"]),
|
||||
TransportApiVersion: envoy_config_core_v3.ApiVersion_V3,
|
||||
GrpcServices: []*envoy_config_core_v3.GrpcService{
|
||||
{
|
||||
TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{
|
||||
EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{
|
||||
ClusterName: "pomerium-control-plane-grpc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
LdsConfig: &envoy_config_core_v3.ConfigSource{
|
||||
ResourceApiVersion: envoy_config_core_v3.ApiVersion_V3,
|
||||
ConfigSourceSpecifier: &envoy_config_core_v3.ConfigSource_Ads{},
|
||||
},
|
||||
CdsConfig: &envoy_config_core_v3.ConfigSource{
|
||||
ResourceApiVersion: envoy_config_core_v3.ApiVersion_V3,
|
||||
ConfigSourceSpecifier: &envoy_config_core_v3.ConfigSource_Ads{},
|
||||
},
|
||||
}
|
||||
|
||||
staticCfg, err := srv.builder.BuildBootstrapStaticResources()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statsCfg, err := srv.builder.BuildBootstrapStatsConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
layeredRuntimeCfg, err := srv.builder.BuildBootstrapLayeredRuntime()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bootstrapCfg := &envoy_config_bootstrap_v3.Bootstrap{
|
||||
Node: nodeCfg,
|
||||
Admin: adminCfg,
|
||||
DynamicResources: dynamicCfg,
|
||||
StaticResources: staticCfg,
|
||||
StatsConfig: statsCfg,
|
||||
LayeredRuntime: layeredRuntimeCfg,
|
||||
}
|
||||
|
||||
jsonBytes, err := protojson.Marshal(bootstrapCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return jsonBytes, nil
|
||||
}
|
||||
|
||||
var fileNameAndNumberRE = regexp.MustCompile(`^(\[[a-zA-Z0-9/-_.]+:[0-9]+])\s(.*)$`)
|
||||
|
||||
func (srv *Server) parseLog(line string) (name string, logLevel string, msg string) {
|
||||
// format: [LOG_FORMAT]level--name--message
|
||||
// message is c-escaped
|
||||
parts := strings.SplitN(line, "--", 3)
|
||||
if len(parts) == 3 {
|
||||
logLevel = strings.TrimPrefix(parts[0], "[LOG_FORMAT]")
|
||||
name = parts[1]
|
||||
msg = parts[2]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (srv *Server) handleLogs(ctx context.Context, rc io.ReadCloser) {
|
||||
defer rc.Close()
|
||||
|
||||
l := log.With().Str("service", "envoy").Logger()
|
||||
bo := backoff.NewExponentialBackOff()
|
||||
|
||||
s := bufio.NewReader(rc)
|
||||
for {
|
||||
ln, err := s.ReadString('\n')
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || errors.Is(err, os.ErrClosed) {
|
||||
break
|
||||
}
|
||||
log.Error(ctx).Err(err).Msg("failed to read log")
|
||||
time.Sleep(bo.NextBackOff())
|
||||
continue
|
||||
}
|
||||
ln = strings.TrimRight(ln, "\r\n")
|
||||
bo.Reset()
|
||||
|
||||
name, logLevel, msg := srv.parseLog(ln)
|
||||
if name == "" {
|
||||
name = "envoy"
|
||||
}
|
||||
|
||||
lvl := zerolog.ErrorLevel
|
||||
if x, err := zerolog.ParseLevel(logLevel); err == nil {
|
||||
lvl = x
|
||||
}
|
||||
if msg == "" {
|
||||
msg = ln
|
||||
}
|
||||
|
||||
msg = fileNameAndNumberRE.ReplaceAllString(msg, "\"$2\"")
|
||||
if s, err := strconv.Unquote(msg); err == nil {
|
||||
msg = s
|
||||
}
|
||||
|
||||
// ignore empty messages
|
||||
if msg == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
l.WithLevel(lvl).
|
||||
Str("name", name).
|
||||
Msg(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) monitorProcess(ctx context.Context, pid int32) {
|
||||
log.Info(ctx).
|
||||
Int32("pid", pid).
|
||||
Msg("envoy: start monitoring subprocess")
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
exists, err := process.PidExistsWithContext(ctx, pid)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).
|
||||
Int32("pid", pid).
|
||||
Msg("envoy: error retrieving subprocess information")
|
||||
} else if !exists {
|
||||
log.Fatal().Err(err).
|
||||
Int32("pid", pid).
|
||||
Msg("envoy: subprocess exited")
|
||||
}
|
||||
|
||||
// wait for the next tick
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
}
|
||||
}
|
29
pkg/envoy/envoy_darwin.go
Normal file
29
pkg/envoy/envoy_darwin.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"syscall"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
)
|
||||
|
||||
var sysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
}
|
||||
|
||||
func (srv *Server) runProcessCollector(ctx context.Context) {}
|
||||
|
||||
func (srv *Server) prepareRunEnvoyCommand(ctx context.Context, sharedArgs []string) (exePath string, args []string) {
|
||||
if srv.cmd != nil && srv.cmd.Process != nil {
|
||||
log.Info(ctx).Msg("envoy: terminating previous envoy process")
|
||||
_ = srv.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
args = make([]string, len(sharedArgs))
|
||||
copy(args, sharedArgs)
|
||||
|
||||
return srv.envoyPath, args
|
||||
}
|
113
pkg/envoy/envoy_linux.go
Normal file
113
pkg/envoy/envoy_linux.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"go.opencensus.io/stats/view"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
||||
)
|
||||
|
||||
const baseIDPath = "/tmp/pomerium-envoy-base-id"
|
||||
|
||||
var restartEpoch struct {
|
||||
sync.Mutex
|
||||
value int
|
||||
}
|
||||
|
||||
var sysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
Pdeathsig: syscall.SIGTERM,
|
||||
}
|
||||
|
||||
func (srv *Server) runProcessCollector(ctx context.Context) {
|
||||
pc := metrics.NewProcessCollector("envoy")
|
||||
if err := view.Register(pc.Views()...); err != nil {
|
||||
log.Error(ctx).Err(err).Msg("failed to register envoy process metric views")
|
||||
}
|
||||
defer view.Unregister(pc.Views()...)
|
||||
|
||||
const collectInterval = time.Second * 10
|
||||
ticker := time.NewTicker(collectInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
}
|
||||
|
||||
var pid int
|
||||
srv.mu.Lock()
|
||||
if srv.cmd != nil && srv.cmd.Process != nil {
|
||||
pid = srv.cmd.Process.Pid
|
||||
}
|
||||
srv.mu.Unlock()
|
||||
|
||||
if pid > 0 {
|
||||
err := pc.Measure(ctx, pid)
|
||||
if err != nil {
|
||||
log.Error(ctx).Err(err).Msg("failed to measure envoy process metrics")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) prepareRunEnvoyCommand(ctx context.Context, sharedArgs []string) (exePath string, args []string) {
|
||||
// release the previous process so we can hot-reload
|
||||
if srv.cmd != nil && srv.cmd.Process != nil {
|
||||
log.Info(ctx).Msg("envoy: releasing envoy process for hot-reload")
|
||||
err := srv.cmd.Process.Release()
|
||||
if err != nil {
|
||||
log.Warn(ctx).Err(err).Str("service", "envoy").Msg("envoy: failed to release envoy process for hot-reload")
|
||||
}
|
||||
}
|
||||
|
||||
args = make([]string, len(sharedArgs))
|
||||
copy(args, sharedArgs)
|
||||
|
||||
restartEpoch.Lock()
|
||||
if baseID, ok := readBaseID(); ok {
|
||||
args = append(args,
|
||||
"--base-id", strconv.Itoa(baseID),
|
||||
"--restart-epoch", strconv.Itoa(restartEpoch.value),
|
||||
"--drain-time-s", "60",
|
||||
"--parent-shutdown-time-s", "120",
|
||||
"--drain-strategy", "immediate",
|
||||
)
|
||||
restartEpoch.value++
|
||||
} else {
|
||||
args = append(args,
|
||||
"--use-dynamic-base-id",
|
||||
"--base-id-path", baseIDPath,
|
||||
)
|
||||
restartEpoch.value = 1
|
||||
}
|
||||
restartEpoch.Unlock()
|
||||
|
||||
return srv.envoyPath, args
|
||||
}
|
||||
|
||||
func readBaseID() (int, bool) {
|
||||
bs, err := os.ReadFile(baseIDPath)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
baseID, err := strconv.Atoi(string(bs))
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return baseID, true
|
||||
}
|
24
pkg/envoy/envoy_other.go
Normal file
24
pkg/envoy/envoy_other.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
//go:build !linux && !darwin
|
||||
// +build !linux,!darwin
|
||||
|
||||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
)
|
||||
|
||||
func (srv *Server) runProcessCollector(ctx context.Context) {}
|
||||
|
||||
func (srv *Server) prepareRunEnvoyCommand(ctx context.Context, sharedArgs []string) (exePath string, args []string) {
|
||||
if srv.cmd != nil && srv.cmd.Process != nil {
|
||||
log.Info(ctx).Msg("envoy: terminating previous envoy process")
|
||||
_ = srv.cmd.Process.Kill()
|
||||
}
|
||||
|
||||
args = make([]string, len(sharedArgs))
|
||||
copy(args, sharedArgs)
|
||||
|
||||
return srv.envoyPath, args
|
||||
}
|
49
pkg/envoy/envoy_test.go
Normal file
49
pkg/envoy/envoy_test.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package envoy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func TestServer_handleLogs(t *testing.T) {
|
||||
logFormatRE := regexp.MustCompile(`^[[]LOG_FORMAT[]](.*?)--(.*?)--(.*?)$`)
|
||||
line := "[LOG_FORMAT]debug--filter--[external/envoy/source/extensions/filters/listener/tls_inspector/tls_inspector.cc:78] tls inspector: new connection accepted"
|
||||
old := func(s string) string {
|
||||
msg := s
|
||||
parts := logFormatRE.FindStringSubmatch(s)
|
||||
if len(parts) == 4 {
|
||||
msg = parts[3]
|
||||
}
|
||||
return msg
|
||||
}
|
||||
srv := &Server{}
|
||||
expectedMsg := old(line)
|
||||
name, logLevel, gotMsg := srv.parseLog(line)
|
||||
if name != "filter" {
|
||||
t.Errorf("unexpected name, want filter, got: %s", name)
|
||||
}
|
||||
if logLevel != "debug" {
|
||||
t.Errorf("unexpected log level, want debug, got: %s", logLevel)
|
||||
}
|
||||
if gotMsg != expectedMsg {
|
||||
t.Errorf("unexpected msg, want %s, got: %s", expectedMsg, gotMsg)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_handleLogs(b *testing.B) {
|
||||
line := `[LOG_FORMAT]debug--http--[external/envoy/source/common/http/conn_manager_impl.cc:781] [C25][S14758077654018620250] request headers complete (end_stream=false):\\n\\':authority\\', \\'enabled-ws-echo.localhost.pomerium.io\\'\\n\\':path\\', \\'/\\'\\n\\':method\\', \\'GET\\'\\n\\'upgrade\\', \\'websocket\\'\\n\\'connection\\', \\'upgrade\\'\\n\\'x-request-id\\', \\'30ac7726e0b9e00a9c9ab2bf66d692ac\\'\\n\\'x-real-ip\\', \\'172.17.0.1\\'\\n\\'x-forwarded-for\\', \\'172.17.0.1\\'\\n\\'x-forwarded-host\\', \\'enabled-ws-echo.localhost.pomerium.io\\'\\n\\'x-forwarded-port\\', \\'443\\'\\n\\'x-forwarded-proto\\', \\'https\\'\\n\\'x-scheme\\', \\'https\\'\\n\\'user-agent\\', \\'Go-http-client/1.1\\'\\n\\'sec-websocket-key\\', \\'4bh7+YFVzrJiblaSu/CVfg==\\'\\n\\'sec-websocket-version\\', \\'13\\'`
|
||||
rc := io.NopCloser(strings.NewReader(line))
|
||||
srv := &Server{}
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for i := 0; i < b.N; i++ {
|
||||
srv.handleLogs(context.Background(), rc)
|
||||
}
|
||||
}
|
99
pkg/envoy/extract.go
Normal file
99
pkg/envoy/extract.go
Normal file
|
@ -0,0 +1,99 @@
|
|||
package envoy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
)
|
||||
|
||||
const (
|
||||
ownerRX = os.FileMode(0o500)
|
||||
maxExpandedEnvoySize = 1 << 30
|
||||
)
|
||||
|
||||
type hashReader struct {
|
||||
hash.Hash
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (hr *hashReader) Read(p []byte) (n int, err error) {
|
||||
n, err = hr.r.Read(p)
|
||||
_, _ = hr.Write(p[:n])
|
||||
return n, err
|
||||
}
|
||||
|
||||
var (
|
||||
setupLock sync.Mutex
|
||||
setupDone bool
|
||||
setupFullEnvoyPath string
|
||||
setupErr error
|
||||
)
|
||||
|
||||
// Extract extracts envoy binary and returns its location
|
||||
func Extract() (fullEnvoyPath string, err error) {
|
||||
setupLock.Lock()
|
||||
defer setupLock.Unlock()
|
||||
|
||||
// if we've extract at least once, and the file we previously extracted no longer exists, force a new extraction
|
||||
if setupFullEnvoyPath != "" {
|
||||
if _, err := os.Stat(setupFullEnvoyPath); os.IsNotExist(err) {
|
||||
setupDone = false
|
||||
}
|
||||
}
|
||||
if setupDone {
|
||||
return setupFullEnvoyPath, setupErr
|
||||
}
|
||||
|
||||
dir, err := os.MkdirTemp(os.TempDir(), "pomerium-envoy")
|
||||
if err != nil {
|
||||
setupErr = fmt.Errorf("envoy: failed making temporary working dir: %w", err)
|
||||
return
|
||||
}
|
||||
setupFullEnvoyPath = filepath.Join(dir, "envoy")
|
||||
|
||||
err = extract(setupFullEnvoyPath)
|
||||
if err != nil {
|
||||
setupErr = fmt.Errorf("envoy: failed to extract embedded envoy binary: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
setupDone = true
|
||||
return setupFullEnvoyPath, setupErr
|
||||
}
|
||||
|
||||
func extract(dstName string) (err error) {
|
||||
checksum, err := hex.DecodeString(strings.Fields(files.Checksum())[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("checksum %s: %w", files.Checksum(), err)
|
||||
}
|
||||
|
||||
hr := &hashReader{
|
||||
Hash: sha256.New(),
|
||||
r: bytes.NewReader(files.Binary()),
|
||||
}
|
||||
|
||||
dst, err := os.OpenFile(dstName, os.O_CREATE|os.O_WRONLY, ownerRX)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { err = dst.Close() }()
|
||||
|
||||
if _, err = io.Copy(dst, io.LimitReader(hr, maxExpandedEnvoySize)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sum := hr.Sum(nil)
|
||||
if !bytes.Equal(sum, checksum) {
|
||||
return fmt.Errorf("expected %x, got %x checksum", checksum, sum)
|
||||
}
|
||||
return nil
|
||||
}
|
27
pkg/envoy/files/files.go
Normal file
27
pkg/envoy/files/files.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Package files contains files for use with envoy.
|
||||
package files
|
||||
|
||||
import (
|
||||
_ "embed" // for embedded files
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Binary returns the raw envoy binary bytes.
|
||||
func Binary() []byte {
|
||||
return rawBinary
|
||||
}
|
||||
|
||||
// Checksum returns the checksum for the embedded envoy binary.
|
||||
func Checksum() string {
|
||||
return strings.Fields(rawChecksum)[0]
|
||||
}
|
||||
|
||||
// FullVersion returns the full version string for envoy.
|
||||
func FullVersion() string {
|
||||
return Version() + "+" + Checksum()
|
||||
}
|
||||
|
||||
// Version returns the envoy version.
|
||||
func Version() string {
|
||||
return strings.TrimSpace(rawVersion)
|
||||
}
|
14
pkg/envoy/files/files_darwin_amd64.go
Normal file
14
pkg/envoy/files/files_darwin_amd64.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
//go:build darwin && amd64 && !embed_pomerium
|
||||
|
||||
package files
|
||||
|
||||
import _ "embed" // embed
|
||||
|
||||
//go:embed envoy-darwin-amd64
|
||||
var rawBinary []byte
|
||||
|
||||
//go:embed envoy-darwin-amd64.sha256
|
||||
var rawChecksum string
|
||||
|
||||
//go:embed envoy-darwin-amd64.version
|
||||
var rawVersion string
|
15
pkg/envoy/files/files_darwin_arm64.go
Normal file
15
pkg/envoy/files/files_darwin_arm64.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build darwin && arm64 && !embed_pomerium
|
||||
// +build darwin,arm64,!embed_pomerium
|
||||
|
||||
package files
|
||||
|
||||
import _ "embed" // embed
|
||||
|
||||
//go:embed envoy-darwin-arm64
|
||||
var rawBinary []byte
|
||||
|
||||
//go:embed envoy-darwin-arm64.sha256
|
||||
var rawChecksum string
|
||||
|
||||
//go:embed envoy-darwin-arm64.version
|
||||
var rawVersion string
|
16
pkg/envoy/files/files_external.go
Normal file
16
pkg/envoy/files/files_external.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
//go:build embed_pomerium
|
||||
|
||||
package files
|
||||
|
||||
var rawBinary []byte
|
||||
|
||||
var rawChecksum string
|
||||
|
||||
var rawVersion string
|
||||
|
||||
// SetFiles sets external source for envoy
|
||||
func SetFiles(bin []byte, checksum, version string) {
|
||||
rawBinary = bin
|
||||
rawChecksum = checksum
|
||||
rawVersion = version
|
||||
}
|
15
pkg/envoy/files/files_linux_amd64.go
Normal file
15
pkg/envoy/files/files_linux_amd64.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build linux && amd64 && !embed_pomerium
|
||||
// +build linux,amd64,!embed_pomerium
|
||||
|
||||
package files
|
||||
|
||||
import _ "embed" // embed
|
||||
|
||||
//go:embed envoy-linux-amd64
|
||||
var rawBinary []byte
|
||||
|
||||
//go:embed envoy-linux-amd64.sha256
|
||||
var rawChecksum string
|
||||
|
||||
//go:embed envoy-linux-amd64.version
|
||||
var rawVersion string
|
15
pkg/envoy/files/files_linux_arm64.go
Normal file
15
pkg/envoy/files/files_linux_arm64.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
//go:build linux && arm64 && !embed_pomerium
|
||||
// +build linux,arm64,!embed_pomerium
|
||||
|
||||
package files
|
||||
|
||||
import _ "embed" // embed
|
||||
|
||||
//go:embed envoy-linux-arm64
|
||||
var rawBinary []byte
|
||||
|
||||
//go:embed envoy-linux-arm64.sha256
|
||||
var rawChecksum string
|
||||
|
||||
//go:embed envoy-linux-arm64.version
|
||||
var rawVersion string
|
37
pkg/envoy/misc.go
Normal file
37
pkg/envoy/misc.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
package envoy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
)
|
||||
|
||||
func firstNonEmpty(args ...string) string {
|
||||
for _, a := range args {
|
||||
if a != "" {
|
||||
return a
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ParseAddress parses a string address into an envoy address.
|
||||
func ParseAddress(raw string) (*envoy_config_core_v3.Address, error) {
|
||||
if host, portstr, err := net.SplitHostPort(raw); err == nil {
|
||||
if port, err := strconv.Atoi(portstr); err == nil {
|
||||
return &envoy_config_core_v3.Address{
|
||||
Address: &envoy_config_core_v3.Address_SocketAddress{
|
||||
SocketAddress: &envoy_config_core_v3.SocketAddress{
|
||||
Address: host,
|
||||
PortSpecifier: &envoy_config_core_v3.SocketAddress_PortValue{
|
||||
PortValue: uint32(port),
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unknown address format: %s", raw)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue