mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-17 19:17:17 +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
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -90,8 +90,8 @@ docs/.vuepress/dist/
|
||||||
.service-accounts
|
.service-accounts
|
||||||
|
|
||||||
/bazel-*
|
/bazel-*
|
||||||
internal/envoy/files/envoy-*-?????
|
pkg/envoy/files/envoy-*-?????
|
||||||
internal/envoy/files/envoy-*-?????.sha256
|
pkg/envoy/files/envoy-*-?????.sha256
|
||||||
internal/envoy/files/envoy-*-?????.version
|
pkg/envoy/files/envoy-*-?????.version
|
||||||
|
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -113,8 +113,8 @@ clean: ## Cleanup any build binaries or packages.
|
||||||
@echo "==> $@"
|
@echo "==> $@"
|
||||||
$(RM) -r $(BINDIR)
|
$(RM) -r $(BINDIR)
|
||||||
$(RM) -r $(BUILDDIR)
|
$(RM) -r $(BUILDDIR)
|
||||||
$(RM) internal/envoy/files/envoy-*
|
$(RM) pkg/envoy/files/envoy-*
|
||||||
$(RM) $GOPATH/bin/protoc-gen-validate
|
$(RM) $(GOPATH)/bin/protoc-gen-validate
|
||||||
$(RM) -r /tmp/pomerium-protoc
|
$(RM) -r /tmp/pomerium-protoc
|
||||||
$(RM) -r /tmp/pomerium-protoc-3pp
|
$(RM) -r /tmp/pomerium-protoc-3pp
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,13 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/internal/cmd/pomerium"
|
"github.com/rs/zerolog"
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/version"
|
"github.com/pomerium/pomerium/internal/version"
|
||||||
|
"github.com/pomerium/pomerium/pkg/cmd/pomerium"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -33,5 +36,16 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(ctx context.Context) error {
|
func run(ctx context.Context) error {
|
||||||
return pomerium.Run(ctx, *configFile)
|
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
||||||
|
return c.Str("config_file_source", *configFile).Bool("bootstrap", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
var src config.Source
|
||||||
|
|
||||||
|
src, err := config.NewFileOrEnvironmentSource(*configFile, files.FullVersion())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return pomerium.Run(ctx, src)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/internal/hashutil"
|
"github.com/pomerium/pomerium/internal/hashutil"
|
||||||
|
"github.com/pomerium/pomerium/internal/netutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config holds pomerium configuration options.
|
// Config holds pomerium configuration options.
|
||||||
|
@ -58,3 +59,19 @@ func (cfg *Config) AllCertificates() ([]tls.Certificate, error) {
|
||||||
func (cfg *Config) Checksum() uint64 {
|
func (cfg *Config) Checksum() uint64 {
|
||||||
return hashutil.MustHash(cfg)
|
return hashutil.MustHash(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllocatePorts populates
|
||||||
|
func (cfg *Config) AllocatePorts() error {
|
||||||
|
ports, err := netutil.AllocatePorts(5)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.GRPCPort = ports[0]
|
||||||
|
cfg.HTTPPort = ports[1]
|
||||||
|
cfg.OutboundPort = ports[2]
|
||||||
|
cfg.MetricsPort = ports[3]
|
||||||
|
cfg.DebugPort = ports[4]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -11,7 +12,6 @@ import (
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/internal/fileutil"
|
"github.com/pomerium/pomerium/internal/fileutil"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/netutil"
|
|
||||||
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -110,26 +110,15 @@ func NewFileOrEnvironmentSource(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ports, err := netutil.AllocatePorts(5)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
grpcPort := ports[0]
|
|
||||||
httpPort := ports[1]
|
|
||||||
outboundPort := ports[2]
|
|
||||||
metricsPort := ports[3]
|
|
||||||
debugPort := ports[4]
|
|
||||||
|
|
||||||
cfg := &Config{
|
cfg := &Config{
|
||||||
Options: options,
|
Options: options,
|
||||||
EnvoyVersion: envoyVersion,
|
EnvoyVersion: envoyVersion,
|
||||||
|
|
||||||
GRPCPort: grpcPort,
|
|
||||||
HTTPPort: httpPort,
|
|
||||||
OutboundPort: outboundPort,
|
|
||||||
MetricsPort: metricsPort,
|
|
||||||
DebugPort: debugPort,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = cfg.AllocatePorts(); err != nil {
|
||||||
|
return nil, fmt.Errorf("allocating ports: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
metrics.SetConfigInfo(ctx, cfg.Options.Services, "local", cfg.Checksum(), true)
|
metrics.SetConfigInfo(ctx, cfg.Options.Services, "local", cfg.Checksum(), true)
|
||||||
|
|
||||||
src := &FileOrEnvironmentSource{
|
src := &FileOrEnvironmentSource{
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/internal/directory"
|
"github.com/pomerium/pomerium/internal/directory"
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
"github.com/pomerium/pomerium/internal/events"
|
"github.com/pomerium/pomerium/internal/events"
|
||||||
"github.com/pomerium/pomerium/internal/identity"
|
"github.com/pomerium/pomerium/internal/identity"
|
||||||
"github.com/pomerium/pomerium/internal/identity/manager"
|
"github.com/pomerium/pomerium/internal/identity/manager"
|
||||||
|
@ -24,6 +23,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/internal/telemetry"
|
"github.com/pomerium/pomerium/internal/telemetry"
|
||||||
"github.com/pomerium/pomerium/internal/version"
|
"github.com/pomerium/pomerium/internal/version"
|
||||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/registry"
|
"github.com/pomerium/pomerium/pkg/grpc/registry"
|
||||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"github.com/pomerium/pomerium/config/envoyconfig"
|
"github.com/pomerium/pomerium/config/envoyconfig"
|
||||||
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
"github.com/pomerium/pomerium/config/envoyconfig/filemgr"
|
||||||
"github.com/pomerium/pomerium/internal/controlplane/xdsmgr"
|
"github.com/pomerium/pomerium/internal/controlplane/xdsmgr"
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
"github.com/pomerium/pomerium/internal/events"
|
"github.com/pomerium/pomerium/internal/events"
|
||||||
"github.com/pomerium/pomerium/internal/httputil/reproxy"
|
"github.com/pomerium/pomerium/internal/httputil/reproxy"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
|
@ -29,6 +28,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/internal/telemetry/requestid"
|
"github.com/pomerium/pomerium/internal/telemetry/requestid"
|
||||||
"github.com/pomerium/pomerium/internal/urlutil"
|
"github.com/pomerium/pomerium/internal/urlutil"
|
||||||
"github.com/pomerium/pomerium/internal/version"
|
"github.com/pomerium/pomerium/internal/version"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||||
pom_grpc "github.com/pomerium/pomerium/pkg/grpc"
|
pom_grpc "github.com/pomerium/pomerium/pkg/grpc"
|
||||||
"github.com/pomerium/pomerium/pkg/grpcutil"
|
"github.com/pomerium/pomerium/pkg/grpcutil"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
package envoy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/natefinch/atomic"
|
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
embeddedEnvoyPermissions fs.FileMode = 0o700
|
|
||||||
embeddedDirectoryPermissions fs.FileMode = 0o755
|
|
||||||
)
|
|
||||||
|
|
||||||
// OverrideEnvoyPath is an override for using an envoy path instead of the embedded envoy path.
|
|
||||||
var OverrideEnvoyPath = ""
|
|
||||||
|
|
||||||
var (
|
|
||||||
embeddedFilesBaseDirectory = filepath.Join(os.TempDir(), "pomerium-embedded-files")
|
|
||||||
extractEmbeddedEnvoyOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
func extractEmbeddedEnvoy(ctx context.Context) (outPath string, err error) {
|
|
||||||
extractEmbeddedEnvoyOnce.Do(func() {
|
|
||||||
// clean up our base directory before starting
|
|
||||||
err = os.RemoveAll(embeddedFilesBaseDirectory)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error cleaning embedded file directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// create known directory base to clean at startup
|
|
||||||
err = os.MkdirAll(embeddedFilesBaseDirectory, embeddedDirectoryPermissions)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error creating embedded file directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// build a random temp directory inside our base directory to guarantee permissions
|
|
||||||
var tmpDir string
|
|
||||||
tmpDir, err = os.MkdirTemp(embeddedFilesBaseDirectory, "envoy-")
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error creating embedded file tmp directory: (directory=%s): %w", embeddedFilesBaseDirectory, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
outPath = filepath.Join(tmpDir, "envoy")
|
|
||||||
|
|
||||||
log.Info(ctx).Str("path", outPath).Msg("extracting envoy binary")
|
|
||||||
err = atomic.WriteFile(outPath, bytes.NewReader(files.Binary()))
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error extracting embedded envoy binary to temporary directory (path=%s): %w", outPath, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Chmod(outPath, embeddedEnvoyPermissions)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("error chmoding embedded envoy binary: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return outPath, err
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/authenticate"
|
"github.com/pomerium/pomerium/authenticate"
|
||||||
|
@ -21,29 +20,22 @@ import (
|
||||||
"github.com/pomerium/pomerium/internal/autocert"
|
"github.com/pomerium/pomerium/internal/autocert"
|
||||||
"github.com/pomerium/pomerium/internal/controlplane"
|
"github.com/pomerium/pomerium/internal/controlplane"
|
||||||
"github.com/pomerium/pomerium/internal/databroker"
|
"github.com/pomerium/pomerium/internal/databroker"
|
||||||
"github.com/pomerium/pomerium/internal/envoy"
|
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
"github.com/pomerium/pomerium/internal/events"
|
"github.com/pomerium/pomerium/internal/events"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/registry"
|
"github.com/pomerium/pomerium/internal/registry"
|
||||||
"github.com/pomerium/pomerium/internal/version"
|
"github.com/pomerium/pomerium/internal/version"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||||
"github.com/pomerium/pomerium/proxy"
|
"github.com/pomerium/pomerium/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Run runs the main pomerium application.
|
// Run runs the main pomerium application.
|
||||||
func Run(ctx context.Context, configFile string) error {
|
func Run(ctx context.Context, src config.Source) error {
|
||||||
log.Info(ctx).
|
log.Info(ctx).
|
||||||
Str("envoy_version", files.FullVersion()).
|
Str("envoy_version", files.FullVersion()).
|
||||||
Str("version", version.FullVersion()).
|
Str("version", version.FullVersion()).
|
||||||
Msg("cmd/pomerium")
|
Msg("cmd/pomerium")
|
||||||
|
|
||||||
var src config.Source
|
|
||||||
|
|
||||||
src, err := config.NewFileOrEnvironmentSource(configFile, files.FullVersion())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
src = databroker.NewConfigSource(ctx, src)
|
src = databroker.NewConfigSource(ctx, src)
|
||||||
logMgr := config.NewLogManager(ctx, src)
|
logMgr := config.NewLogManager(ctx, src)
|
||||||
defer logMgr.Close()
|
defer logMgr.Close()
|
||||||
|
@ -51,7 +43,7 @@ func Run(ctx context.Context, configFile string) error {
|
||||||
// trigger changes when underlying files are changed
|
// trigger changes when underlying files are changed
|
||||||
src = config.NewFileWatcherSource(src)
|
src = config.NewFileWatcherSource(src)
|
||||||
|
|
||||||
src, err = autocert.New(src)
|
src, err := autocert.New(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -78,9 +70,7 @@ func Run(ctx context.Context, configFile string) error {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if err = controlPlane.OnConfigChange(log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
if err = controlPlane.OnConfigChange(ctx, src.GetConfig()); err != nil {
|
||||||
return c.Str("config_file_source", configFile).Bool("bootstrap", true)
|
|
||||||
}), src.GetConfig()); err != nil {
|
|
||||||
return fmt.Errorf("applying config: %w", err)
|
return fmt.Errorf("applying config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,36 @@
|
||||||
package pomerium
|
package pomerium_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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) {
|
func Test_run(t *testing.T) {
|
||||||
os.Clearenv()
|
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 {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
configFileFlag string
|
configFileFlag string
|
||||||
wantErr bool
|
check func(require.TestingT, error, ...any)
|
||||||
}{
|
}{
|
||||||
{"nil configuration", "", true},
|
{"nil configuration", "", require.Error},
|
||||||
{"bad proxy no authenticate url", `
|
{"bad proxy no authenticate url", `
|
||||||
{
|
{
|
||||||
"address": ":9433",
|
"address": ":9433",
|
||||||
|
@ -26,7 +42,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "proxy",
|
"services": "proxy",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"bad authenticate no cookie secret", `
|
{"bad authenticate no cookie secret", `
|
||||||
{
|
{
|
||||||
"address": ":9433",
|
"address": ":9433",
|
||||||
|
@ -37,7 +53,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "authenticate",
|
"services": "authenticate",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"bad authorize service bad shared key", `
|
{"bad authorize service bad shared key", `
|
||||||
{
|
{
|
||||||
"address": ":9433",
|
"address": ":9433",
|
||||||
|
@ -49,7 +65,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "authorize",
|
"services": "authorize",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"bad http port", `
|
{"bad http port", `
|
||||||
{
|
{
|
||||||
"address": ":-1",
|
"address": ":-1",
|
||||||
|
@ -63,7 +79,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "proxy",
|
"services": "proxy",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"bad redirect port", `
|
{"bad redirect port", `
|
||||||
{
|
{
|
||||||
"address": ":9433",
|
"address": ":9433",
|
||||||
|
@ -78,7 +94,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "proxy",
|
"services": "proxy",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"bad metrics port ", `
|
{"bad metrics port ", `
|
||||||
{
|
{
|
||||||
"address": ":9433",
|
"address": ":9433",
|
||||||
|
@ -92,7 +108,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "proxy",
|
"services": "proxy",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
{"malformed tracing provider", `
|
{"malformed tracing provider", `
|
||||||
{
|
{
|
||||||
"tracing_provider": "bad tracing provider",
|
"tracing_provider": "bad tracing provider",
|
||||||
|
@ -107,7 +123,7 @@ func Test_run(t *testing.T) {
|
||||||
"services": "proxy",
|
"services": "proxy",
|
||||||
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
"policy": [{ "from": "https://pomerium.io", "to": "https://httpbin.org" }]
|
||||||
}
|
}
|
||||||
`, true},
|
`, require.Error},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
@ -115,7 +131,7 @@ func Test_run(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("Cannot create temporary file", err)
|
t.Fatal("Cannot create temporary file", err)
|
||||||
}
|
}
|
||||||
defer os.Remove(tmpFile.Name())
|
defer func() { _ = os.Remove(tmpFile.Name()) }()
|
||||||
fn := tmpFile.Name()
|
fn := tmpFile.Name()
|
||||||
if _, err := tmpFile.Write([]byte(tt.configFileFlag)); err != nil {
|
if _, err := tmpFile.Write([]byte(tt.configFileFlag)); err != nil {
|
||||||
tmpFile.Close()
|
tmpFile.Close()
|
||||||
|
@ -126,10 +142,7 @@ func Test_run(t *testing.T) {
|
||||||
ctx, clearTimeout := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
ctx, clearTimeout := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||||
defer clearTimeout()
|
defer clearTimeout()
|
||||||
|
|
||||||
err = Run(ctx, configFile)
|
tt.check(t, run(ctx, configFile))
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("run() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,13 +5,12 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -30,14 +29,13 @@ import (
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/config"
|
"github.com/pomerium/pomerium/config"
|
||||||
"github.com/pomerium/pomerium/config/envoyconfig"
|
"github.com/pomerium/pomerium/config/envoyconfig"
|
||||||
"github.com/pomerium/pomerium/internal/envoy/files"
|
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/telemetry"
|
"github.com/pomerium/pomerium/internal/telemetry"
|
||||||
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
workingDirectoryName = ".pomerium-envoy"
|
configFileName = "envoy-config.yaml"
|
||||||
configFileName = "envoy-config.yaml"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type serverOptions struct {
|
type serverOptions struct {
|
||||||
|
@ -62,38 +60,13 @@ type Server struct {
|
||||||
|
|
||||||
// NewServer creates a new server with traffic routed by envoy.
|
// NewServer creates a new server with traffic routed by envoy.
|
||||||
func NewServer(ctx context.Context, src config.Source, builder *envoyconfig.Builder) (*Server, error) {
|
func NewServer(ctx context.Context, src config.Source, builder *envoyconfig.Builder) (*Server, error) {
|
||||||
wd := filepath.Join(os.TempDir(), workingDirectoryName)
|
envoyPath, err := Extract()
|
||||||
err := os.MkdirAll(wd, embeddedEnvoyPermissions)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating temporary working directory for envoy: %w", err)
|
return nil, fmt.Errorf("extracting envoy: %w", err)
|
||||||
}
|
|
||||||
|
|
||||||
envoyPath := OverrideEnvoyPath
|
|
||||||
if envoyPath == "" {
|
|
||||||
envoyPath, err = extractEmbeddedEnvoy(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error extracting embedded envoy binary: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checksum is written at build time, if it's not empty we verify the binary
|
|
||||||
if files.Checksum() != "" {
|
|
||||||
bs, err := os.ReadFile(envoyPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error reading envoy binary for checksum verification: %w", err)
|
|
||||||
}
|
|
||||||
h := sha256.New()
|
|
||||||
h.Write(bs)
|
|
||||||
s := hex.EncodeToString(h.Sum(nil))
|
|
||||||
if files.Checksum() != s {
|
|
||||||
return nil, fmt.Errorf("invalid envoy binary, expected %s but got %s", files.Checksum(), s)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Info(ctx).Msg("no checksum defined, envoy binary will not be verified!")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := &Server{
|
srv := &Server{
|
||||||
wd: wd,
|
wd: path.Dir(envoyPath),
|
||||||
builder: builder,
|
builder: builder,
|
||||||
grpcPort: src.GetConfig().GRPCPort,
|
grpcPort: src.GetConfig().GRPCPort,
|
||||||
httpPort: src.GetConfig().HTTPPort,
|
httpPort: src.GetConfig().HTTPPort,
|
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
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
//go:build darwin && amd64
|
//go:build darwin && amd64 && !embed_pomerium
|
||||||
// +build darwin,amd64
|
|
||||||
|
|
||||||
package files
|
package files
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build darwin && arm64
|
//go:build darwin && arm64 && !embed_pomerium
|
||||||
// +build darwin,arm64
|
// +build darwin,arm64,!embed_pomerium
|
||||||
|
|
||||||
package files
|
package files
|
||||||
|
|
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
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build linux && amd64
|
//go:build linux && amd64 && !embed_pomerium
|
||||||
// +build linux,amd64
|
// +build linux,amd64,!embed_pomerium
|
||||||
|
|
||||||
package files
|
package files
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//go:build linux && arm64
|
//go:build linux && arm64 && !embed_pomerium
|
||||||
// +build linux,arm64
|
// +build linux,arm64,!embed_pomerium
|
||||||
|
|
||||||
package files
|
package files
|
||||||
|
|
|
@ -6,22 +6,24 @@ export PATH
|
||||||
|
|
||||||
_project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)/.."
|
_project_root="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)/.."
|
||||||
_envoy_version=1.21.3
|
_envoy_version=1.21.3
|
||||||
_dir="$_project_root/internal/envoy/files"
|
_dir="$_project_root/pkg/envoy/files"
|
||||||
_target="${TARGET:-"$(go env GOOS)-$(go env GOARCH)"}"
|
_target="${TARGET:-"$(go env GOOS)-$(go env GOARCH)"}"
|
||||||
|
|
||||||
_url="https://github.com/pomerium/envoy-binaries/releases/download/v${_envoy_version}/envoy-${_target}"
|
_url="https://github.com/pomerium/envoy-binaries/releases/download/v${_envoy_version}/envoy-${_target}"
|
||||||
|
|
||||||
curl \
|
curl \
|
||||||
--compressed \
|
|
||||||
--silent \
|
--silent \
|
||||||
|
--fail \
|
||||||
|
--compressed \
|
||||||
--location \
|
--location \
|
||||||
--time-cond "$_dir/envoy-$_target" \
|
--time-cond "$_dir/envoy-$_target" \
|
||||||
--output "$_dir/envoy-$_target" \
|
--output "$_dir/envoy-$_target" \
|
||||||
"$_url"
|
"$_url"
|
||||||
|
|
||||||
curl \
|
curl \
|
||||||
--compressed \
|
|
||||||
--silent \
|
--silent \
|
||||||
|
--fail \
|
||||||
|
--compressed \
|
||||||
--location \
|
--location \
|
||||||
--time-cond "$_dir/envoy-$_target.sha256" \
|
--time-cond "$_dir/envoy-$_target.sha256" \
|
||||||
--output "$_dir/envoy-$_target.sha256" \
|
--output "$_dir/envoy-$_target.sha256" \
|
||||||
|
|
37
ui/embed.go
37
ui/embed.go
|
@ -3,27 +3,18 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"embed"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pomerium/csrf"
|
"github.com/pomerium/csrf"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
//go:embed dist/*
|
|
||||||
uiFS embed.FS
|
|
||||||
)
|
|
||||||
|
|
||||||
// ServeFile serves a file.
|
// ServeFile serves a file.
|
||||||
func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error {
|
func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error {
|
||||||
f, etag, err := openLocalOrEmbeddedFile(filepath.Join("dist", filePath))
|
f, etag, err := openFile(filepath.Join("dist", filePath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -47,7 +38,7 @@ func ServePage(w http.ResponseWriter, r *http.Request, page string, data map[str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
f, _, err := openLocalOrEmbeddedFile("dist/index.html")
|
f, _, err := openFile("dist/index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -67,27 +58,3 @@ func ServePage(w http.ResponseWriter, r *http.Request, page string, data map[str
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTime = time.Now()
|
var startTime = time.Now()
|
||||||
|
|
||||||
func openLocalOrEmbeddedFile(name string) (f fs.File, etag string, err error) {
|
|
||||||
f, err = os.Open(filepath.Join("ui", name))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
f, err = uiFS.Open(name)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
_ = f.Close()
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
modTime := fi.ModTime()
|
|
||||||
if modTime.IsZero() {
|
|
||||||
modTime = startTime
|
|
||||||
}
|
|
||||||
etag = fmt.Sprintf("%x", modTime.UnixNano())
|
|
||||||
|
|
||||||
return f, etag, nil
|
|
||||||
}
|
|
||||||
|
|
37
ui/embed_ext.go
Normal file
37
ui/embed_ext.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
//go:build embed_pomerium
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ExtUIFS must be set to provide access to UI dist/ files
|
||||||
|
ExtUIFS fs.FS
|
||||||
|
)
|
||||||
|
|
||||||
|
func openFile(name string) (f fs.File, etag string, err error) {
|
||||||
|
if ExtUIFS == nil {
|
||||||
|
return nil, "", fmt.Errorf("ui package was incorrectly compiled with embed_pomerium yet no FS was provided")
|
||||||
|
}
|
||||||
|
f, err = ExtUIFS.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("open %s: %w", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
modTime := fi.ModTime()
|
||||||
|
if modTime.IsZero() {
|
||||||
|
modTime = startTime
|
||||||
|
}
|
||||||
|
etag = fmt.Sprintf("%x", modTime.UnixNano())
|
||||||
|
|
||||||
|
return f, etag, nil
|
||||||
|
}
|
40
ui/embed_local.go
Normal file
40
ui/embed_local.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//go:build !embed_pomerium
|
||||||
|
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed dist/*
|
||||||
|
uiFS embed.FS
|
||||||
|
)
|
||||||
|
|
||||||
|
func openFile(name string) (f fs.File, etag string, err error) {
|
||||||
|
f, err = os.Open(filepath.Join("ui", name))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
f, err = uiFS.Open(name)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
_ = f.Close()
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
modTime := fi.ModTime()
|
||||||
|
if modTime.IsZero() {
|
||||||
|
modTime = startTime
|
||||||
|
}
|
||||||
|
etag = fmt.Sprintf("%x", modTime.UnixNano())
|
||||||
|
|
||||||
|
return f, etag, nil
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue