mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-03 16:59:22 +02:00
initial core-zero import implementation
This commit is contained in:
parent
c011957389
commit
b598d139e5
34 changed files with 3825 additions and 688 deletions
105
internal/zero/cmd/command_import.go
Normal file
105
internal/zero/cmd/command_import.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func BuildImportCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import an existing configuration to a Zero cluster",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
configFlag := cmd.InheritedFlags().Lookup("config")
|
||||
var configFile string
|
||||
if configFlag != nil {
|
||||
configFile = configFlag.Value.String()
|
||||
}
|
||||
if configFile == "" {
|
||||
// try looking up what pid 1 is using, we are likely in a container anyway
|
||||
info, err := os.ReadFile("/proc/1/cmdline")
|
||||
if err == nil {
|
||||
args := bytes.Split(info, []byte{0})
|
||||
if len(args) > 0 && strings.Contains(string(args[0]), "pomerium") {
|
||||
for i, arg := range args {
|
||||
if strings.Contains(string(arg), "-config") {
|
||||
if strings.Contains(string(arg), "-config=") {
|
||||
configFile = strings.Split(string(arg), "=")[1]
|
||||
cmd.PrintErrf("detected config file: %s\n", configFile)
|
||||
} else if len(args) > i+1 {
|
||||
configFile = string(args[i+1])
|
||||
cmd.PrintErrf("detected config file: %s\n", configFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try some common locations
|
||||
if configFile == "" {
|
||||
if _, err := os.Stat("/pomerium/config.yaml"); err == nil {
|
||||
configFile = "/pomerium/config.yaml"
|
||||
} else if _, err := os.Stat("/etc/pomerium/config.yaml"); err == nil {
|
||||
configFile = "/etc/pomerium/config.yaml"
|
||||
} else if _, err := os.Stat("config.yaml"); err == nil {
|
||||
configFile = "config.yaml"
|
||||
}
|
||||
|
||||
if configFile != "" {
|
||||
cmd.PrintErrf("detected config file: %s\n", configFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
if configFile == "" {
|
||||
return fmt.Errorf("no config file provided")
|
||||
}
|
||||
log.SetLevel(zerolog.ErrorLevel)
|
||||
src, err := config.NewFileOrEnvironmentSource(configFile, files.FullVersion())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgC := make(chan *config.Config, 1)
|
||||
src.OnConfigChange(cmd.Context(), func(_ context.Context, cfg *config.Config) {
|
||||
cfgC <- cfg
|
||||
})
|
||||
if cfg := src.GetConfig(); cfg != nil {
|
||||
cfgC <- cfg
|
||||
}
|
||||
|
||||
var cfg *config.Config
|
||||
select {
|
||||
case <-cmd.Context().Done():
|
||||
return cmd.Context().Err()
|
||||
case cfg = <-cfgC:
|
||||
}
|
||||
|
||||
client := zeroClientFromContext(cmd.Context())
|
||||
quotas, err := client.GetQuotas(cmd.Context())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting quotas: %w", err)
|
||||
}
|
||||
converted := cfg.Options.ToProto()
|
||||
ui := NewImportUI(converted, quotas)
|
||||
if err := ui.Run(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
ui.ApplySelections(converted)
|
||||
_, err = client.ImportConfig(cmd.Context(), converted)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error importing config: %w", err)
|
||||
}
|
||||
cmd.PrintErrln("config imported successfully")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
}
|
71
internal/zero/cmd/command_root.go
Normal file
71
internal/zero/cmd/command_root.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
zero "github.com/pomerium/pomerium/internal/zero/api"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type zeroClientContextKeyType struct{}
|
||||
|
||||
var zeroClientContextKey zeroClientContextKeyType
|
||||
|
||||
func zeroClientFromContext(ctx context.Context) *zero.API {
|
||||
return ctx.Value(zeroClientContextKey).(*zero.API)
|
||||
}
|
||||
|
||||
func BuildRootCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "zero",
|
||||
Short: "Interact with the Pomerium Zero cloud service",
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
configFlag := cmd.InheritedFlags().Lookup("config")
|
||||
var configFile string
|
||||
if configFlag != nil {
|
||||
configFile = configFlag.Value.String()
|
||||
}
|
||||
|
||||
if err := setupLogger(); err != nil {
|
||||
return err
|
||||
}
|
||||
var token string
|
||||
if tokenFlag := cmd.InheritedFlags().Lookup("token"); tokenFlag != nil && tokenFlag.Changed {
|
||||
token = tokenFlag.Value.String()
|
||||
} else {
|
||||
token = getToken(configFile)
|
||||
}
|
||||
if token == "" {
|
||||
return errors.New("no token provided")
|
||||
}
|
||||
|
||||
var clusterAPIEndpoint string
|
||||
if endpointFlag := cmd.InheritedFlags().Lookup("cluster-api-endpoint"); endpointFlag != nil && endpointFlag.Changed {
|
||||
clusterAPIEndpoint = endpointFlag.Value.String()
|
||||
} else {
|
||||
clusterAPIEndpoint = getClusterAPIEndpoint()
|
||||
}
|
||||
|
||||
client, err := zero.NewAPI(cmd.Context(),
|
||||
zero.WithAPIToken(token),
|
||||
zero.WithClusterAPIEndpoint(clusterAPIEndpoint),
|
||||
zero.WithConnectAPIEndpoint(getConnectAPIEndpoint()),
|
||||
zero.WithOTELEndpoint(getOTELAPIEndpoint()),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.SetContext(context.WithValue(cmd.Context(), zeroClientContextKey, client))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(BuildImportCmd())
|
||||
cmd.PersistentFlags().String("config", "", "Specify configuration file location")
|
||||
cmd.PersistentFlags().String("token", "", "Pomerium Zero Token (default: $POMERIUM_ZERO_TOKEN)")
|
||||
cmd.PersistentFlags().String("cluster-api-endpoint", "", "Pomerium Zero Cluster API Endpoint (default: $CLUSTER_API_ENDPOINT)")
|
||||
cmd.PersistentFlags().Lookup("cluster-api-endpoint").Hidden = true
|
||||
|
||||
return cmd
|
||||
}
|
7
internal/zero/cmd/export_test.go
Normal file
7
internal/zero/cmd/export_test.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package cmd
|
||||
|
||||
import "github.com/charmbracelet/huh"
|
||||
|
||||
func (ui *ImportUI) XForm() *huh.Form {
|
||||
return ui.form
|
||||
}
|
538
internal/zero/cmd/import_ui.go
Normal file
538
internal/zero/cmd/import_ui.go
Normal file
|
@ -0,0 +1,538 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
http_connection_managerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
"github.com/muesli/termenv"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/internal/log"
|
||||
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
|
||||
cluster_api "github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
"github.com/pomerium/pomerium/pkg/zero/importutil"
|
||||
"github.com/pomerium/protoutil/fieldmasks"
|
||||
"github.com/pomerium/protoutil/paths"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/reflect/protopath"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
)
|
||||
|
||||
type onCursorUpdate struct {
|
||||
Field interface{ Cursor() int }
|
||||
}
|
||||
|
||||
func (u onCursorUpdate) Hash() (uint64, error) {
|
||||
return uint64(u.Field.Cursor()), nil
|
||||
}
|
||||
|
||||
var (
|
||||
yellowText = lipgloss.NewStyle().Foreground(lipgloss.ANSIColor(3))
|
||||
faintText = lipgloss.NewStyle().Faint(true).UnsetForeground()
|
||||
redText = lipgloss.NewStyle().Foreground(lipgloss.ANSIColor(1))
|
||||
)
|
||||
|
||||
func errText(err error) string {
|
||||
return redText.Render(fmt.Sprintf("(error: %v)", err))
|
||||
}
|
||||
|
||||
func certInfoFromSettingsCertificate(v protoreflect.Value) string {
|
||||
switch v := v.Interface().(type) {
|
||||
case protoreflect.List:
|
||||
buf := bytes.Buffer{}
|
||||
for i, l := 0, v.Len(); i < l; i++ {
|
||||
crtBytes := string(v.Get(i).Message().Interface().(*configpb.Settings_Certificate).GetCertBytes())
|
||||
buf.WriteString(crtBytes)
|
||||
if i < l-1 {
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
return certInfoFromBytes(buf.Bytes())
|
||||
case protoreflect.Message:
|
||||
crtBytes := string(v.Interface().(*configpb.Settings_Certificate).GetCertBytes())
|
||||
return certInfoFromBytes([]byte(crtBytes))
|
||||
default:
|
||||
panic(fmt.Sprintf("bug: unexpected value type %T", v))
|
||||
}
|
||||
}
|
||||
|
||||
func certInfoFromBase64(v protoreflect.Value) string {
|
||||
crtBytes, err := base64.StdEncoding.DecodeString(v.String())
|
||||
if err != nil {
|
||||
return errText(err)
|
||||
}
|
||||
return certInfoFromBytes(crtBytes)
|
||||
}
|
||||
|
||||
func certInfoFromBytes(b []byte) string {
|
||||
if len(b) == 0 {
|
||||
return faintText.Render("(empty)")
|
||||
}
|
||||
block, rest := pem.Decode(b)
|
||||
if block == nil {
|
||||
return errText(errors.New("no PEM data found"))
|
||||
}
|
||||
extraBlocks := []*pem.Block{}
|
||||
for len(rest) > 0 {
|
||||
block, rest = pem.Decode(rest)
|
||||
if block != nil {
|
||||
extraBlocks = append(extraBlocks, block)
|
||||
}
|
||||
}
|
||||
blockType := block.Type
|
||||
var info string
|
||||
switch block.Type {
|
||||
case "X509 CRL":
|
||||
crl, err := x509.ParseRevocationList(block.Bytes)
|
||||
if err != nil {
|
||||
return errText(err)
|
||||
}
|
||||
info = fmt.Sprintf("%d entries", len(crl.RevokedCertificateEntries))
|
||||
default:
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return errText(err)
|
||||
}
|
||||
info = *importutil.GenerateCertName(cert)
|
||||
}
|
||||
out := yellowText.Render(fmt.Sprintf("(%s: %s)", blockType, info))
|
||||
if len(extraBlocks) > 0 {
|
||||
s := ""
|
||||
if len(extraBlocks) != 1 {
|
||||
s = "s"
|
||||
}
|
||||
out += faintText.Render(fmt.Sprintf(" ...+%d block%s", len(extraBlocks), s))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func secret(s protoreflect.Value) string {
|
||||
length := len(s.String())
|
||||
return yellowText.Render(fmt.Sprintf("(secret: %d bytes)", length))
|
||||
}
|
||||
|
||||
var customSettingsInfoByPath = map[string]func(v protoreflect.Value) string{
|
||||
"(pomerium.config.Settings).metrics_certificate": certInfoFromSettingsCertificate,
|
||||
"(pomerium.config.Settings).metrics_client_ca": certInfoFromBase64,
|
||||
"(pomerium.config.Settings).certificates": certInfoFromSettingsCertificate,
|
||||
"(pomerium.config.Settings).certificate_authority": certInfoFromBase64,
|
||||
"(pomerium.config.Settings).downstream_mtls.ca": certInfoFromBase64,
|
||||
"(pomerium.config.Settings).downstream_mtls.crl": certInfoFromBase64,
|
||||
"(pomerium.config.Settings).shared_secret": secret,
|
||||
"(pomerium.config.Settings).cookie_secret": secret,
|
||||
"(pomerium.config.Settings).google_cloud_serverless_authentication_service_account": secret,
|
||||
"(pomerium.config.Settings).idp_client_secret": secret,
|
||||
"(pomerium.config.Settings).databroker_storage_connection_string": secret,
|
||||
}
|
||||
|
||||
type ImportHints struct {
|
||||
// Indicates that the field is ignored during Zero import
|
||||
Ignored bool
|
||||
// Indicates that the field is entirely unsupported by Zero, and will likely
|
||||
// break an existing configuration if imported. If any of these fields are
|
||||
// selected, an error will be displayed.
|
||||
Unsupported bool
|
||||
// An optional note explaining why a field is ignored or unsupported, if
|
||||
// additional context would be helpful. This message will be user facing.
|
||||
Note string
|
||||
// Indicates that the field is treated as a secret, and will be encrypted.
|
||||
Secret bool
|
||||
}
|
||||
|
||||
const (
|
||||
noteSplitService = "split-service mode"
|
||||
noteEnterpriseOnly = "enterprise only"
|
||||
noteFeatureNotYetAvailable = "feature not yet available"
|
||||
)
|
||||
|
||||
func noteCertificate(n int) string {
|
||||
suffix := ""
|
||||
if n != 1 {
|
||||
suffix = "s"
|
||||
}
|
||||
return fmt.Sprintf("+%d certificate%s", n, suffix)
|
||||
}
|
||||
|
||||
func notePolicy(n int) string {
|
||||
suffix := "y"
|
||||
if n != 1 {
|
||||
suffix = "ies"
|
||||
}
|
||||
return fmt.Sprintf("+%d polic%s", n, suffix)
|
||||
}
|
||||
|
||||
func computeSettingsImportHints(cfg *configpb.Config) map[string]ImportHints {
|
||||
m := map[string]ImportHints{
|
||||
"authenticate_callback_path": {Ignored: true},
|
||||
"shared_secret": {Ignored: true},
|
||||
"cookie_secret": {Ignored: true},
|
||||
"signing_key": {Ignored: true},
|
||||
"authenticate_internal_service_url": {Unsupported: true, Note: noteSplitService},
|
||||
"authorize_internal_service_url": {Unsupported: true, Note: noteSplitService},
|
||||
"databroker_internal_service_url": {Unsupported: true, Note: noteSplitService},
|
||||
"derive_tls": {Unsupported: true, Note: noteSplitService},
|
||||
"audit_key": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"primary_color": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"secondary_color": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"darkmode_primary_color": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"darkmode_secondary_color": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"logo_url": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"favicon_url": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"error_message_first_paragraph": {Unsupported: true, Note: noteEnterpriseOnly},
|
||||
"use_proxy_protocol": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"programmatic_redirect_domain_whitelist": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"grpc_client_timeout": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"grpc_client_dns_roundrobin": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"envoy_bind_config_freebind": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"envoy_bind_config_source_address": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"google_cloud_serverless_authentication_service_account": {Secret: true},
|
||||
"idp_client_secret": {Secret: true},
|
||||
"databroker_storage_connection_string": {Secret: true},
|
||||
"metrics_certificate": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"metrics_client_ca": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
// "metrics_certificate": {Note: noteCertificate(1)},
|
||||
// "metrics_client_ca": {Note: noteCertificate(1)},
|
||||
"certificate_authority": {Note: noteCertificate(1)},
|
||||
"certificates": {Note: noteCertificate(len(cfg.GetSettings().GetCertificates()))},
|
||||
"downstream_mtls.crl": {Unsupported: true, Note: noteFeatureNotYetAvailable},
|
||||
"downstream_mtls.ca": {Note: noteCertificate(1)},
|
||||
}
|
||||
if dm := cfg.GetSettings().GetDownstreamMtls(); dm != nil {
|
||||
if dm.Enforcement != nil {
|
||||
switch *dm.Enforcement {
|
||||
case configpb.MtlsEnforcementMode_POLICY:
|
||||
case configpb.MtlsEnforcementMode_POLICY_WITH_DEFAULT_DENY:
|
||||
case configpb.MtlsEnforcementMode_REJECT_CONNECTION:
|
||||
// this is a special case - zero does not support this mode, but we cannot continue
|
||||
// with a partial import because it fundamentally changes the behavior of all routes
|
||||
// and policies in the system
|
||||
log.Fatal().Msg("downstream mtls enforcement mode 'reject_connection' is not supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
if cfg.GetSettings().GetServices() != "all" {
|
||||
m["services"] = ImportHints{Ignored: true, Note: `only "all" is supported`}
|
||||
}
|
||||
if cfg.GetSettings().GetCodecType() != http_connection_managerv3.HttpConnectionManager_AUTO {
|
||||
m["codec_type"] = ImportHints{Ignored: true, Note: `only "auto" is supported`}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
type ImportUI struct {
|
||||
form *huh.Form
|
||||
selectedSettings []string
|
||||
selectedRoutes []string
|
||||
}
|
||||
|
||||
func NewImportUI(cfg *configpb.Config, quotas *cluster_api.ConfigQuotas) *ImportUI {
|
||||
settingsImportHints := computeSettingsImportHints(cfg)
|
||||
|
||||
presentSettings := fieldmasks.Leaves(
|
||||
fieldmasks.Diff(
|
||||
config.NewDefaultOptions().ToProto().GetSettings().ProtoReflect(),
|
||||
cfg.GetSettings().ProtoReflect(),
|
||||
),
|
||||
cfg.Settings.ProtoReflect().Descriptor(),
|
||||
)
|
||||
slices.Sort(presentSettings.Paths)
|
||||
settingsOptions := huh.NewOptions(presentSettings.Paths...)
|
||||
|
||||
ui := &ImportUI{
|
||||
selectedSettings: slices.Clone(presentSettings.Paths),
|
||||
}
|
||||
|
||||
for i, value := range presentSettings.Paths {
|
||||
if hints, ok := settingsImportHints[value]; ok {
|
||||
switch {
|
||||
case hints.Ignored:
|
||||
note := ""
|
||||
if hints.Note != "" {
|
||||
note = fmt.Sprintf(": %s", hints.Note)
|
||||
}
|
||||
settingsOptions[i].Key = fmt.Sprintf("\x1b[9m%s\x1b[29m \x1b[2m(ignored%s)\x1b[22m", settingsOptions[i].Key, note)
|
||||
ui.selectedSettings[i] = ""
|
||||
case hints.Unsupported:
|
||||
note := ""
|
||||
if hints.Note != "" {
|
||||
note = fmt.Sprintf(": %s", hints.Note)
|
||||
}
|
||||
settingsOptions[i].Key = fmt.Sprintf("\x1b[9m%s\x1b[29m \x1b[2m(unsupported%s)\x1b[22m", settingsOptions[i].Key, note)
|
||||
ui.selectedSettings[i] = ""
|
||||
case hints.Secret:
|
||||
settingsOptions[i].Key += " \x1b[2m(secret)\x1b[22m"
|
||||
default:
|
||||
if hints.Note != "" {
|
||||
settingsOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", hints.Note)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ui.selectedSettings = slices.DeleteFunc(ui.selectedSettings, func(s string) bool {
|
||||
return s == ""
|
||||
})
|
||||
settingsSelect := huh.NewMultiSelect[string]().
|
||||
Filterable(false).
|
||||
Title("Import Settings").
|
||||
Description("Choose settings to import from your existing configuration").
|
||||
Options(settingsOptions...).
|
||||
Validate(func(selected []string) error {
|
||||
var unsupportedCount int
|
||||
for _, s := range selected {
|
||||
if hints, ok := settingsImportHints[s]; ok && hints.Unsupported {
|
||||
unsupportedCount++
|
||||
}
|
||||
}
|
||||
if unsupportedCount == 1 {
|
||||
return fmt.Errorf("1 selected setting is unsupported")
|
||||
} else if unsupportedCount > 1 {
|
||||
return fmt.Errorf("%d selected settings are unsupported", unsupportedCount)
|
||||
}
|
||||
return nil
|
||||
}).
|
||||
Value(&ui.selectedSettings)
|
||||
settingsSelect.Focus()
|
||||
|
||||
escapeNoteText := strings.NewReplacer(
|
||||
"*", "\\*",
|
||||
"_", "\\_",
|
||||
"`", "\\`",
|
||||
)
|
||||
settingsNoteDescription := func(idx int) string {
|
||||
if idx < 0 || idx > len(presentSettings.Paths) {
|
||||
return ""
|
||||
}
|
||||
path, err := paths.ParseFrom(cfg.Settings.ProtoReflect().Descriptor(), "."+presentSettings.Paths[idx])
|
||||
if err != nil {
|
||||
return errText(err)
|
||||
}
|
||||
val, err := paths.Evaluate(cfg.Settings, path)
|
||||
if err != nil {
|
||||
return errText(err)
|
||||
}
|
||||
if infoFunc, ok := customSettingsInfoByPath[path.String()]; ok {
|
||||
return escapeNoteText.Replace(infoFunc(val))
|
||||
}
|
||||
return escapeNoteText.Replace(formatValue(path, val))
|
||||
}
|
||||
settingsNote := huh.NewNote().
|
||||
Title(fmt.Sprintf("Value: %s", presentSettings.Paths[0])).
|
||||
TitleFunc(func() string {
|
||||
return fmt.Sprintf("Value: %s", presentSettings.Paths[settingsSelect.Cursor()])
|
||||
}, onCursorUpdate{settingsSelect}).
|
||||
Description(settingsNoteDescription(0)).
|
||||
DescriptionFunc(func() string {
|
||||
return settingsNoteDescription(settingsSelect.Cursor())
|
||||
}, onCursorUpdate{settingsSelect}).
|
||||
Height(3)
|
||||
settingsNote.Focus()
|
||||
|
||||
routeNames := make([]string, len(cfg.Routes))
|
||||
for i, name := range importutil.GenerateRouteNames(cfg.Routes) {
|
||||
routeNames[i] = name
|
||||
cfg.Routes[i].Name = name
|
||||
}
|
||||
routeOptions := huh.NewOptions(routeNames...)
|
||||
for i, name := range routeNames {
|
||||
if i < quotas.Routes {
|
||||
ui.selectedRoutes = append(ui.selectedRoutes, name)
|
||||
}
|
||||
if n := includedCertificatesInRoute(cfg.Routes[i]); n > 0 {
|
||||
routeOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", noteCertificate(n))
|
||||
}
|
||||
if n := includedPoliciesInRoute(cfg.Routes[i]); n > 0 {
|
||||
routeOptions[i].Key += fmt.Sprintf(" \x1b[2m(%s)\x1b[22m", notePolicy(n))
|
||||
}
|
||||
}
|
||||
|
||||
routesSelectDescription := func() string {
|
||||
return fmt.Sprintf(`
|
||||
Choose routes to import from your existing configuration. Policies and
|
||||
certificates associated with selected routes will also be imported.
|
||||
|
||||
Pomerium Zero routes require unique names. We've generated default names
|
||||
from the contents of each route, but these can always be changed later on.
|
||||
|
||||
Selected: %d/%d`[1:], len(ui.selectedRoutes), quotas.Routes)
|
||||
}
|
||||
topMarginLines := 1 + len(strings.Split(routesSelectDescription(), "\n"))
|
||||
routesSelect := huh.NewMultiSelect[string]().
|
||||
Filterable(true).
|
||||
Title("Import Routes").
|
||||
Description(routesSelectDescription()).
|
||||
DescriptionFunc(routesSelectDescription, &ui.selectedRoutes).
|
||||
Height(min(30, len(cfg.Routes)) + topMarginLines).
|
||||
Options(routeOptions...).
|
||||
Validate(func(_ []string) error {
|
||||
if len(ui.selectedRoutes) > quotas.Routes {
|
||||
return fmt.Errorf("A maximum of %d routes can be imported", quotas.Routes) //nolint:stylecheck
|
||||
}
|
||||
return nil
|
||||
}).
|
||||
Value(&ui.selectedRoutes)
|
||||
|
||||
var (
|
||||
labelFrom = yellowText.Render(" from: ")
|
||||
labelPath = yellowText.Render(" path: ")
|
||||
labelPrefix = yellowText.Render(" prefix: ")
|
||||
labelRegex = yellowText.Render(" regex: ")
|
||||
labelTo = yellowText.Render(" to: ")
|
||||
labelRedirect = yellowText.Render("redirect: ")
|
||||
labelResponse = yellowText.Render("response: ")
|
||||
)
|
||||
routesNoteDescription := func(idx int) string {
|
||||
selected := cfg.Routes[idx]
|
||||
var b strings.Builder
|
||||
b.WriteString(labelFrom)
|
||||
b.WriteString(selected.From)
|
||||
switch {
|
||||
case selected.Path != "":
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelPath)
|
||||
b.WriteString(selected.Path)
|
||||
case selected.Prefix != "":
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelPrefix)
|
||||
b.WriteString(selected.Prefix)
|
||||
case selected.Regex != "":
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelRegex)
|
||||
b.WriteString(selected.Regex)
|
||||
}
|
||||
switch {
|
||||
case len(selected.To) > 0:
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelTo)
|
||||
b.WriteString(selected.To[0])
|
||||
for _, t := range selected.To[1:] {
|
||||
b.WriteString(", ")
|
||||
b.WriteString(t)
|
||||
}
|
||||
case selected.Redirect != nil:
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelRedirect)
|
||||
b.WriteString(selected.Redirect.String())
|
||||
case selected.Response != nil:
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(labelResponse)
|
||||
b.WriteString(fmt.Sprint(selected.Response.Status))
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(strconv.Quote(selected.Response.Body))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
routesNote := huh.NewNote().
|
||||
Title("Route Info").
|
||||
Description(routesNoteDescription(0)).
|
||||
DescriptionFunc(func() string {
|
||||
return routesNoteDescription(routesSelect.Cursor())
|
||||
}, onCursorUpdate{routesSelect}).Height(3)
|
||||
routesNote.Focus()
|
||||
|
||||
ui.form = huh.NewForm(
|
||||
huh.NewGroup(settingsSelect, settingsNote),
|
||||
huh.NewGroup(routesSelect, routesNote),
|
||||
).WithTheme(huh.ThemeBase16())
|
||||
return ui
|
||||
}
|
||||
|
||||
func (ui *ImportUI) Run(ctx context.Context) error {
|
||||
if lipgloss.ColorProfile() == termenv.Ascii &&
|
||||
!termenv.EnvNoColor() && os.Getenv("TERM") != "dumb" {
|
||||
lipgloss.SetColorProfile(termenv.ANSI)
|
||||
}
|
||||
return ui.form.RunWithContext(ctx)
|
||||
}
|
||||
|
||||
func (ui *ImportUI) ApplySelections(cfg *configpb.Config) {
|
||||
fieldmasks.ExclusiveKeep(cfg.Settings, &fieldmaskpb.FieldMask{
|
||||
Paths: ui.selectedSettings,
|
||||
})
|
||||
cfg.Routes = slices.DeleteFunc(cfg.Routes, func(route *configpb.Route) bool {
|
||||
return !slices.Contains(ui.selectedRoutes, route.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func includedCertificatesInRoute(route *configpb.Route) int {
|
||||
n := 0
|
||||
if route.TlsClientCert != "" && route.TlsClientKey != "" {
|
||||
n++
|
||||
}
|
||||
if route.TlsCustomCa != "" {
|
||||
n++
|
||||
}
|
||||
if route.TlsDownstreamClientCa != "" {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func includedPoliciesInRoute(route *configpb.Route) int {
|
||||
n := 0
|
||||
for _, policy := range route.PplPolicies {
|
||||
// skip over common generated policies
|
||||
switch string(policy.Raw) {
|
||||
case `[{"allow":{"or":[{"accept":true}]}}]`:
|
||||
case `[{"allow":{"or":[{"authenticated_user":true}]}}]`:
|
||||
case `[{"allow":{"or":[{"cors_preflight":true}]}}]`:
|
||||
default:
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func formatValue(path protopath.Path, val protoreflect.Value) string {
|
||||
switch vi := val.Interface().(type) {
|
||||
case protoreflect.Message:
|
||||
jsonData, err := protojson.Marshal(vi.Interface())
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return string(jsonData)
|
||||
case protoreflect.List:
|
||||
values := []string{}
|
||||
for i := 0; i < vi.Len(); i++ {
|
||||
values = append(values, formatValue(path, vi.Get(i)))
|
||||
}
|
||||
return renderStringSlice(values)
|
||||
case protoreflect.Map:
|
||||
values := []string{}
|
||||
vi.Range(func(mk protoreflect.MapKey, v protoreflect.Value) bool {
|
||||
values = append(values, mk.String()+yellowText.Render("=")+formatValue(path, v))
|
||||
return true
|
||||
})
|
||||
slices.Sort(values)
|
||||
return renderStringSlice(values)
|
||||
case protoreflect.EnumNumber:
|
||||
var field protoreflect.FieldDescriptor
|
||||
switch step := path.Index(-1); step.Kind() {
|
||||
case protopath.FieldAccessStep:
|
||||
field = step.FieldDescriptor()
|
||||
case protopath.ListIndexStep, protopath.MapIndexStep:
|
||||
field = path.Index(-2).FieldDescriptor()
|
||||
}
|
||||
if field != nil {
|
||||
return strings.ToLower(string(field.Enum().Values().ByNumber(vi).Name()))
|
||||
}
|
||||
return fmt.Sprint(vi)
|
||||
default:
|
||||
return val.String()
|
||||
}
|
||||
}
|
||||
|
||||
func renderStringSlice(values []string) string {
|
||||
return yellowText.Render("[") + strings.Join(values, yellowText.Render(", ")) + yellowText.Render("]")
|
||||
}
|
112
internal/zero/cmd/import_ui_test.go
Normal file
112
internal/zero/cmd/import_ui_test.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package cmd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/pomerium/pomerium/config"
|
||||
"github.com/pomerium/pomerium/pkg/envoy/files"
|
||||
"github.com/pomerium/pomerium/pkg/zero/cluster"
|
||||
"github.com/pomerium/pomerium/pkg/zero/importutil"
|
||||
"github.com/pomerium/protoutil/fieldmasks"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/charmbracelet/x/exp/teatest"
|
||||
"github.com/pomerium/pomerium/internal/zero/cmd"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
//go:embed testdata
|
||||
var testdata embed.FS
|
||||
|
||||
func TestImportUI(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
require.NoError(t, os.CopyFS(tmp, testdata))
|
||||
dir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(dir)
|
||||
os.Chdir(filepath.Join(tmp, "testdata"))
|
||||
|
||||
src, err := config.NewFileOrEnvironmentSource("config.yaml", files.FullVersion())
|
||||
require.NoError(t, err)
|
||||
|
||||
cfgC := make(chan *config.Config, 1)
|
||||
src.OnConfigChange(context.Background(), func(_ context.Context, cfg *config.Config) {
|
||||
cfgC <- cfg
|
||||
})
|
||||
if cfg := src.GetConfig(); cfg != nil {
|
||||
cfgC <- cfg
|
||||
}
|
||||
cfg := (<-cfgC).Options.ToProto()
|
||||
|
||||
ui := cmd.NewImportUI(cfg, &cluster.ConfigQuotas{
|
||||
Certificates: 10,
|
||||
Policies: 10,
|
||||
Routes: 10,
|
||||
})
|
||||
|
||||
form := ui.XForm()
|
||||
form.SubmitCmd = tea.Quit
|
||||
form.CancelCmd = tea.Quit
|
||||
|
||||
tm := teatest.NewTestModel(t, form, teatest.WithInitialTermSize(80, 80))
|
||||
|
||||
presentSettings := fieldmasks.Leaves(
|
||||
fieldmasks.Diff(
|
||||
config.NewDefaultOptions().ToProto().GetSettings().ProtoReflect(),
|
||||
cfg.GetSettings().ProtoReflect(),
|
||||
),
|
||||
cfg.Settings.ProtoReflect().Descriptor(),
|
||||
)
|
||||
slices.Sort(presentSettings.Paths)
|
||||
|
||||
for i, setting := range presentSettings.Paths {
|
||||
if i > 0 {
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
|
||||
}
|
||||
var foundSelect bool
|
||||
teatest.WaitFor(t, tm.Output(), func(bts []byte) bool {
|
||||
str := ansi.Strip(string(bts))
|
||||
if !foundSelect {
|
||||
if strings.Contains(str, fmt.Sprintf("> [•] %s", setting)) ||
|
||||
strings.Contains(str, fmt.Sprintf("> [ ] %s", setting)) {
|
||||
foundSelect = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
return strings.Contains(str, fmt.Sprintf("Value: %s", setting))
|
||||
}, teatest.WithDuration(1*time.Second), teatest.WithCheckInterval(1*time.Millisecond))
|
||||
}
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyTab})
|
||||
names := importutil.GenerateRouteNames(cfg.Routes)
|
||||
for i, route := range cfg.Routes {
|
||||
if i > 0 {
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
|
||||
}
|
||||
var foundSelect bool
|
||||
teatest.WaitFor(t, tm.Output(), func(bts []byte) bool {
|
||||
str := ansi.Strip(string(bts))
|
||||
if !foundSelect {
|
||||
if strings.Contains(str, fmt.Sprintf("> [•] %s", names[i])) ||
|
||||
strings.Contains(str, fmt.Sprintf("> [ ] %s", names[i])) {
|
||||
foundSelect = true
|
||||
}
|
||||
return false
|
||||
}
|
||||
if i == 0 || cfg.Routes[i-1].From != route.From {
|
||||
return strings.Contains(str, fmt.Sprintf("from: %s", route.From))
|
||||
}
|
||||
return true
|
||||
}, teatest.WithDuration(1*time.Second), teatest.WithCheckInterval(1*time.Millisecond))
|
||||
}
|
||||
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
|
||||
tm.WaitFinished(t)
|
||||
}
|
28
internal/zero/cmd/testdata/ca.crt
vendored
Normal file
28
internal/zero/cmd/testdata/ca.crt
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1zCCAz+gAwIBAgIQZ139cd/paPdkS2JyAu7kEDANBgkqhkiG9w0BAQsFADCB
|
||||
gzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSwwKgYDVQQLDCNjYWxl
|
||||
YkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTEzMDEGA1UEAwwqbWtjZXJ0
|
||||
IGNhbGViQGNhbGViLXBjLWxpbnV4IChDYWxlYiBEb3hzZXkpMB4XDTIxMDgxMDE3
|
||||
MzIwOVoXDTMxMDgxMDE3MzIwOVowgYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9w
|
||||
bWVudCBDQTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXggKENhbGViIERv
|
||||
eHNleSkxMzAxBgNVBAMMKm1rY2VydCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2Fs
|
||||
ZWIgRG94c2V5KTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANbKyMz5
|
||||
MVW6YKdjh1oIN1Mn7PE2pH5SbJSpWxdAGhdBkBkpAa7OxarjH5KVkCTSa7oncla7
|
||||
qNuJZS6mBmoxF+R+cR3jyGdUAYlozl1jlfqLIfC/+g7V7VmOJn98tjB42fatxLl6
|
||||
WPAw1JDNsWtQfhKhbcHut7RsF0rMOOHcwywTR7LOyCmIel1pcmpV4hbVcT6eVwoP
|
||||
HXyJSa9cqaMQ5Xrdogai4IqZZIGLHeLsTVutOgJFXEevlX/QT3sWomEctzh38Js4
|
||||
9DiAPD6d4Y7/CPLYEfk29JQ9NZhpgDsi9hu5FHHZcXwf1IHlw/CBVgn6j+jmvKKz
|
||||
90Ma1oquv3W6dttid/xCcLGu2S+96Tzrykmoy5VacLtVEP41YmoVls91rlo7olpe
|
||||
QWFbnmco739TI/4h+HodolperQERQl7uCnpKVPZ3WokKuRh5pkqkQp/arQjtwcRt
|
||||
G43CrDpbl+uSjMCAxha958eTYvtojTMnvLtsGID1hGXnqlw+5KjKrgRHrQIDAQAB
|
||||
o0UwQzAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
|
||||
FgQUhYZYWIBHyk6ZVTnp3lRt/tyBP00wDQYJKoZIhvcNAQELBQADggGBAA1F/apr
|
||||
l6pNT3Mp/MxhUUgo6usEJCryGQcLRfexyQXGN3huCmIrP55VFa8ETPAtjsr6PMe7
|
||||
7vvEj8eFu2JtKovlQwNewYU9cjAMCVaFiNbrQa20hzhWc2js6dyildE6/DPzbeds
|
||||
KDAxhFNp35SlwtRtKk1SzxJxsqSwjfxI8fp+R/0wO8g0fWTdM2gCpRwYMNwJELEg
|
||||
+dSlvJCwuu+rzxLalzaPF1PMTW72OELal/j5sD+2VytQ4k+HUDbyt2DnQT7YQ3zo
|
||||
q02x2u2sm1WW/o/uh8pjPxkGQqL2mryZs6VH9VCU3QkKNDssNd71lr3wPoE4YRHe
|
||||
UvzD1eDeelzBUFNIpDCjdCsL55yIPqUsr6lmjpBPL0vea33QTMbcsSxu0umGXDbU
|
||||
66juU4Z1jOE0wClIvaO699J+E2gBe1jUN6At6b8BSoZqCqXYoDHGei9RBUdvgqto
|
||||
kVsoJfDI/TFMekYgpL5UVYmLdfgqLPPRP9pQBLDx3mszeAqnvfTICAzfXg==
|
||||
-----END CERTIFICATE-----
|
179
internal/zero/cmd/testdata/config.yaml
vendored
Normal file
179
internal/zero/cmd/testdata/config.yaml
vendored
Normal file
|
@ -0,0 +1,179 @@
|
|||
authenticate_service_url: https://authenticate.localhost.pomerium.io
|
||||
certificate_file: tls.crt
|
||||
certificate_authority_file: ca.crt
|
||||
certificate_key_file: tls.key
|
||||
cookie_secret: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=
|
||||
databroker_storage_connection_string: postgres://pomerium:password@postgres:5432/test
|
||||
databroker_storage_type: postgres
|
||||
downstream_mtls:
|
||||
crl_file: crl.pem
|
||||
envoy_admin_address: 0.0.0.0:9091
|
||||
google_cloud_serverless_authentication_service_account: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ==
|
||||
idp_client_id: CLIENT_ID
|
||||
idp_client_secret: CLIENT_SECRET
|
||||
idp_provider: oidc
|
||||
idp_provider_url: https://mock-idp.localhost.pomerium.io/
|
||||
jwt_claims_headers: email,groups,user
|
||||
log_level: debug
|
||||
routes:
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://mock-idp.localhost.pomerium.io
|
||||
preserve_host_header: true
|
||||
to: http://mock-idp:8024
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://envoy.localhost.pomerium.io
|
||||
to: http://localhost:9901
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://verify.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
to: http://verify:80
|
||||
- allow_public_unauthenticated_access: true
|
||||
allow_websockets: true
|
||||
from: https://websocket-echo.localhost.pomerium.io
|
||||
to: http://websocket-echo:80
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://fortio-ui.localhost.pomerium.io
|
||||
to: https://fortio:8080
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://fortio-ping.localhost.pomerium.io
|
||||
tls_custom_ca_file: route_ca_1.crt
|
||||
tls_server_name: fortio-ping.localhost.pomerium.io
|
||||
to: https://fortio:8079
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails-ip-address.localhost.pomerium.io
|
||||
to: https://172.21.0.50:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-skip-verify-enabled
|
||||
tls_skip_verify: true
|
||||
to: https://trusted-httpdetails:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-skip-verify-disabled
|
||||
tls_skip_verify: false
|
||||
to: https://trusted-httpdetails:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-server-name-enabled
|
||||
tls_server_name: httpdetails.localhost.notpomerium.io
|
||||
to: https://wrongly-named-httpdetails:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-server-name-disabled
|
||||
to: https://wrongly-named-httpdetails:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-custom-ca-enabled
|
||||
tls_custom_ca_file: route_ca_2.crt
|
||||
tls_server_name: httpdetails.localhost.pomerium.io
|
||||
to: https://untrusted-httpdetails:8443
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
path: /tls-custom-ca-disabled
|
||||
to: https://untrusted-httpdetails:8443
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://client-cert-required.localhost.pomerium.io
|
||||
tls_downstream_client_ca_file: route_downstream_ca_1.crt
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://client-cert-overlap.localhost.pomerium.io
|
||||
path: /ca1
|
||||
tls_downstream_client_ca_file: route_downstream_ca_1.crt
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://client-cert-overlap.localhost.pomerium.io
|
||||
path: /ca2
|
||||
tls_downstream_client_ca_file: route_downstream_ca_2.crt
|
||||
to: http://trusted-httpdetails:8080
|
||||
- cors_allow_preflight: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /cors-enabled
|
||||
to: http://trusted-httpdetails:8080
|
||||
- cors_allow_preflight: false
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /cors-disabled
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /preserve-host-header-enabled
|
||||
preserve_host_header: true
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /preserve-host-header-disabled
|
||||
preserve_host_header: false
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://restricted-httpdetails.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
to: http://trusted-httpdetails:8080
|
||||
- from: https://ppl-restricted-httpdetails.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
to: http://trusted-httpdetails:8080
|
||||
policy:
|
||||
- allow:
|
||||
or:
|
||||
- email:
|
||||
is: foo@example.com
|
||||
- email:
|
||||
is: bar@example.com
|
||||
- allowed_domains:
|
||||
- dogs.test
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
prefix: /by-domain
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allowed_users:
|
||||
- user1@dogs.test
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
prefix: /by-user
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /round-robin
|
||||
to:
|
||||
- http://trusted-1-httpdetails:8080
|
||||
- http://trusted-2-httpdetails:8080
|
||||
- http://trusted-3-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /ring-hash
|
||||
to:
|
||||
- http://trusted-1-httpdetails:8080
|
||||
- http://trusted-2-httpdetails:8080
|
||||
- http://trusted-3-httpdetails:8080
|
||||
- allow_any_authenticated_user: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
prefix: /maglev
|
||||
to:
|
||||
- http://trusted-1-httpdetails:8080
|
||||
- http://trusted-2-httpdetails:8080
|
||||
- http://trusted-3-httpdetails:8080
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://httpdetails.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
set_request_headers:
|
||||
X-Custom-Request-Header: custom-request-header-value
|
||||
to: http://trusted-httpdetails:8080
|
||||
- allow_public_unauthenticated_access: true
|
||||
allow_websockets: true
|
||||
from: https://enabled-ws-echo.localhost.pomerium.io
|
||||
to: http://websocket-echo:80
|
||||
- allow_public_unauthenticated_access: true
|
||||
from: https://disabled-ws-echo.localhost.pomerium.io
|
||||
to: http://websocket-echo:80
|
||||
- allow_public_unauthenticated_access: true
|
||||
enable_google_cloud_serverless_authentication: true
|
||||
from: https://cloudrun.localhost.pomerium.io
|
||||
pass_identity_headers: true
|
||||
set_request_headers:
|
||||
x-idp: oidc
|
||||
to: http://trusted-httpdetails:8080
|
||||
- from: https://200.localhost.pomerium.io
|
||||
response:
|
||||
status: 200
|
||||
body: OK
|
||||
shared_secret: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=
|
||||
|
||||
tls_derive: example.com # unsupported
|
29
internal/zero/cmd/testdata/crl.pem
vendored
Normal file
29
internal/zero/cmd/testdata/crl.pem
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
-----BEGIN X509 CRL-----
|
||||
MIICWjCBwwIBATANBgkqhkiG9w0BAQsFADA6MR4wHAYDVQQKExVta2NlcnQgZGV2
|
||||
ZWxvcG1lbnQgQ0ExGDAWBgNVBAMTD2Rvd25zdHJlYW0gQ0EgMRcNMjMwNzE5MjEx
|
||||
ODQ1WhcNMzMwNzE2MjExODQ1WjAjMCECEEY9jnU8Vkt2MYueskRd7bwXDTIzMDcx
|
||||
OTIxMTc0N1qgMDAuMB8GA1UdIwQYMBaAFNH1NAz8Uj24PhCGdBkGi0CMQGMLMAsG
|
||||
A1UdFAQEAgIQADANBgkqhkiG9w0BAQsFAAOCAYEA4w3ow4j1DaufiBBXhdC0ECyY
|
||||
zDxOuACdR4zyoYbjN1g2kc0buchJ7+V0eTY/RnSNc+uqNY+LYprXQquZKlr9dFUr
|
||||
vJ/pXJ+uyLR/MzehiTr3HoTLCPliKZDDayPmoZvaqHD8IoGEnQX6kCEhopb7gtqJ
|
||||
U7TfHaexi0p43FH00gnZfaDMkcAd8zClsEXUrAFCQRD1M5PuCOTO7CeQcI53uBvd
|
||||
8aGvyHlKA/2O17gniMngcoCO72NAUltJzMbugqeXOoiGHYoSsKTbY7MdLhY3MEBa
|
||||
3ZkCFgt3HLHTz5S0PeBVrT7/y7Sz5cj0QA0JKL3J3psngVbpS4oHu6cyvg//7NdG
|
||||
KNBqdas+KPAsmV+3y64Cr2hnv+WsWjiuxDgIEFzpQOcyNOZzmISACw7YXjwFuIne
|
||||
OiiMuYs/2NvwQ1OPfq3jg3If8kBUcSVh+Te4FI3+07tWUvN6nVYC4VmXAcG1HuxQ
|
||||
Gnne9f5hgEJPVfLT+uJ31VV16+vBnZD85DZJTrDM
|
||||
-----END X509 CRL-----
|
||||
-----BEGIN X509 CRL-----
|
||||
MIICNTCBngIBATANBgkqhkiG9w0BAQsFADA6MR4wHAYDVQQKExVta2NlcnQgZGV2
|
||||
ZWxvcG1lbnQgQ0ExGDAWBgNVBAMTD2Rvd25zdHJlYW0gQ0EgMhcNMjMwNzE5MjE1
|
||||
MDE1WhcNMzMwNzE2MjE1MDE1WqAwMC4wHwYDVR0jBBgwFoAUCxQ2cBa5YzqVzamp
|
||||
iNCx8KwFFyQwCwYDVR0UBAQCAhAAMA0GCSqGSIb3DQEBCwUAA4IBgQCYamx8pM+R
|
||||
Clyskcu7ouhu/R1Jy1nWGyWtKphYq0XFbOLlnk2Z7eDfAX8Eej2FavqxzapR2x2O
|
||||
4iJNDCmiwYYYUS2X2LJ3rRRJXyXvWhtfHrxURd6BitC2IXpykBtVlf3zAnZ8GZFQ
|
||||
S1jdfyLMuEAiDwIai3Yt8HsDp/qG089oXcoStyQg/uRpmWy05A9uCVOfNHSLSZu8
|
||||
lr4qatleu0wWbV1amL8tO9x4CRkO0o1YaQq4DoOruPr+3NkTmPvGidh3F71V6IEA
|
||||
h+KzdbRXxFmCCWLWmpJDcrgR7KUqZOhUUt+DUqaqhV44qI0nrpR+QZLohoDor9Lw
|
||||
K+ufj3n29eSRX+3Px+oVWPT8YZP2uKPdizi96me2jWTr51x9AjEoJDsTnYRl9+uY
|
||||
ShiUxWnTdQsooknIfcS/0zfgZ87GvUVzinCQzJpwVxd4Alt8AlR+fXAqNIoOguyv
|
||||
p/CtRVnjVE7l7HW/hQRq1J0ijCCKwmyf/KTd6EK4TdrvbX/U9msVM8Y=
|
||||
-----END X509 CRL-----
|
28
internal/zero/cmd/testdata/route_ca_1.crt
vendored
Normal file
28
internal/zero/cmd/testdata/route_ca_1.crt
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIE1zCCAz+gAwIBAgIQZ139cd/paPdkS2JyAu7kEDANBgkqhkiG9w0BAQsFADCB
|
||||
gzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSwwKgYDVQQLDCNjYWxl
|
||||
YkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTEzMDEGA1UEAwwqbWtjZXJ0
|
||||
IGNhbGViQGNhbGViLXBjLWxpbnV4IChDYWxlYiBEb3hzZXkpMB4XDTIxMDgxMDE3
|
||||
MzIwOVoXDTMxMDgxMDE3MzIwOVowgYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9w
|
||||
bWVudCBDQTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXggKENhbGViIERv
|
||||
eHNleSkxMzAxBgNVBAMMKm1rY2VydCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2Fs
|
||||
ZWIgRG94c2V5KTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBANbKyMz5
|
||||
MVW6YKdjh1oIN1Mn7PE2pH5SbJSpWxdAGhdBkBkpAa7OxarjH5KVkCTSa7oncla7
|
||||
qNuJZS6mBmoxF+R+cR3jyGdUAYlozl1jlfqLIfC/+g7V7VmOJn98tjB42fatxLl6
|
||||
WPAw1JDNsWtQfhKhbcHut7RsF0rMOOHcwywTR7LOyCmIel1pcmpV4hbVcT6eVwoP
|
||||
HXyJSa9cqaMQ5Xrdogai4IqZZIGLHeLsTVutOgJFXEevlX/QT3sWomEctzh38Js4
|
||||
9DiAPD6d4Y7/CPLYEfk29JQ9NZhpgDsi9hu5FHHZcXwf1IHlw/CBVgn6j+jmvKKz
|
||||
90Ma1oquv3W6dttid/xCcLGu2S+96Tzrykmoy5VacLtVEP41YmoVls91rlo7olpe
|
||||
QWFbnmco739TI/4h+HodolperQERQl7uCnpKVPZ3WokKuRh5pkqkQp/arQjtwcRt
|
||||
G43CrDpbl+uSjMCAxha958eTYvtojTMnvLtsGID1hGXnqlw+5KjKrgRHrQIDAQAB
|
||||
o0UwQzAOBgNVHQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4E
|
||||
FgQUhYZYWIBHyk6ZVTnp3lRt/tyBP00wDQYJKoZIhvcNAQELBQADggGBAA1F/apr
|
||||
l6pNT3Mp/MxhUUgo6usEJCryGQcLRfexyQXGN3huCmIrP55VFa8ETPAtjsr6PMe7
|
||||
7vvEj8eFu2JtKovlQwNewYU9cjAMCVaFiNbrQa20hzhWc2js6dyildE6/DPzbeds
|
||||
KDAxhFNp35SlwtRtKk1SzxJxsqSwjfxI8fp+R/0wO8g0fWTdM2gCpRwYMNwJELEg
|
||||
+dSlvJCwuu+rzxLalzaPF1PMTW72OELal/j5sD+2VytQ4k+HUDbyt2DnQT7YQ3zo
|
||||
q02x2u2sm1WW/o/uh8pjPxkGQqL2mryZs6VH9VCU3QkKNDssNd71lr3wPoE4YRHe
|
||||
UvzD1eDeelzBUFNIpDCjdCsL55yIPqUsr6lmjpBPL0vea33QTMbcsSxu0umGXDbU
|
||||
66juU4Z1jOE0wClIvaO699J+E2gBe1jUN6At6b8BSoZqCqXYoDHGei9RBUdvgqto
|
||||
kVsoJfDI/TFMekYgpL5UVYmLdfgqLPPRP9pQBLDx3mszeAqnvfTICAzfXg==
|
||||
-----END CERTIFICATE-----
|
28
internal/zero/cmd/testdata/route_ca_2.crt
vendored
Normal file
28
internal/zero/cmd/testdata/route_ca_2.crt
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIE2DCCA0CgAwIBAgIRALd9GaJR92qi7qL1eHGM6K0wDQYJKoZIhvcNAQELBQAw
|
||||
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
|
||||
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
|
||||
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMTA4MTEy
|
||||
MTU2MTBaFw0zMTA4MTEyMTU2MTBaMIGDMR4wHAYDVQQKExVta2NlcnQgZGV2ZWxv
|
||||
cG1lbnQgQ0ExLDAqBgNVBAsMI2NhbGViQGNhbGViLXBjLWxpbnV4IChDYWxlYiBE
|
||||
b3hzZXkpMTMwMQYDVQQDDCpta2NlcnQgY2FsZWJAY2FsZWItcGMtbGludXggKENh
|
||||
bGViIERveHNleSkwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDWYpVe
|
||||
BSnee2cABYofSoWxGMyFaMQ0nJkY0UWM9ckyUh7VfgN+/aFSW2ZSmXuv5drcpi20
|
||||
z3elhPTe98bANbj+/bi0015QWnMenK05ZK6qDtFwo/HVC/Ycaruu96+1J2toeWuE
|
||||
tykW3MCpC1pHYS5g9iVDkpdrznvXKlYuSikjrj7K5toiTvum97LxKkuj6DXjapPD
|
||||
5vteSN1dQgO9CS3sqlcwYA6RjUHwY2VEh2adP37BZrZwO+yJq9qF5y5Glgi8lN4c
|
||||
KlIlFUs/xSpQsxNbNQXtN9mk4imYlZGzYYbbm+foBVPPboa5jVwKDpZ65mOs7JGP
|
||||
6yj+7V7UBMFpW+gKmJtgh/kkAx185h93qwLFPc8/T7n++P1bu+fakXPGPE21rDeL
|
||||
PnUmucIZpJo5NpYVQv4WvTKq/zMR9Sspz2PFJnERTfTvq+F1q3ZNafEziPsB9oeS
|
||||
njxwmaZOSV0vXq/qeoqx4v6MBzVAY0/8R2LcpJ4ug0OZ3w0b2t6yo86P5Q8CAwEA
|
||||
AaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0O
|
||||
BBYEFLcY8EoNofMcrrxzyxIn3W6ZOMVXMA0GCSqGSIb3DQEBCwUAA4IBgQCZzDCv
|
||||
KIHX3GvjNSY5w5bOn4E3w7QHP09ABjT/wuT4LDkZHJMmlrLo3s8bcsQ0sMD1Y///
|
||||
s07cp4xYlqD7BA0AcpvYVYq58xKxsoCwVXmG5cEeOoZmWf3qY2mS8eW96vOFrdIb
|
||||
L4OF4xYUOMRqAOGAAr6VlO7gXa406HzrsA1hYZwreXhOTCZZPZOUnAu05SHFdgaM
|
||||
TJNB/o01tpwQlrTxNmfropoOzyuvH0zU2RrMs0+EbOuC4A2cQ83DIFxvq67lyU0A
|
||||
s1Q6tRM0+UDmJOLz3SdgN+D00hcuuj92GV4bH8BfyUv8NCY0vDij0TSjj4c4Qtc7
|
||||
IPLTZ2g545oczhNgAmT7d+B5InyfiSIKemXqes2jpiAfzPNl9BVxsakcs/YzoYs1
|
||||
+qTjAWuaDsKohEnO4BJuzv0xrce40enRgXyGGFvXu2s4FY2vJqTSo6ysDWnhI3LW
|
||||
dcg6O2F4APCGGe7zsuqiqkpcknBabgzEs9foHq2mfo7XiEzedMN8BNqfSbA=
|
||||
-----END CERTIFICATE-----
|
25
internal/zero/cmd/testdata/route_downstream_ca_1.crt
vendored
Normal file
25
internal/zero/cmd/testdata/route_downstream_ca_1.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEPDCCAqSgAwIBAgIJAKmtj1u+hOdzMA0GCSqGSIb3DQEBCwUAMDoxHjAcBgNV
|
||||
BAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEYMBYGA1UEAxMPZG93bnN0cmVhbSBD
|
||||
QSAxMB4XDTIzMDYwODE4NTgyMloXDTMzMDYwODE4NTgyMlowOjEeMBwGA1UEChMV
|
||||
bWtjZXJ0IGRldmVsb3BtZW50IENBMRgwFgYDVQQDEw9kb3duc3RyZWFtIENBIDEw
|
||||
ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDue3fuI704DazewdWmKJQs
|
||||
YGYR2ZapQQeQynXaqOhqMOLTc7M18uVOnfhvFVtUB5OCtxL2TMmy8/ytIQlU8CUc
|
||||
bUo1AFcXu1MGORJNu5zbJymsrOE8fKqopb3muGNRM6tulIHhpRCcF3m8pKFBZBWs
|
||||
CR7A2MhgKHJvd1yVMc6/GpO/RqIHiFAiCV9XguadKTwapPJ54vJwBDZoDM4/qA34
|
||||
xFR1uCAzob0D4yFW/C7u57SMZDjSy2jxxZkcFQAvmRPPgzutaAHuRUUnPhw3f9PF
|
||||
+DLNDeo6kXdS6aQOb/weCPl/VjlskXyvgNuzGE2xixZYBQwpXAE8AuBcXNvlxT0T
|
||||
1oyoU8aggymnTFWnLmN/ipQ7+9CHS2+apFDG7nrf9q5UgLtRiVLOytoVxWDOhoY5
|
||||
pqbS05aDjWXbXyPf2e318Ntjc6Hl7nSffHlCGsb/zqiJnJX6ti/k0VR1WHJZyu7e
|
||||
CYeu+mtqNATrS7h+nBUMNZ9Bb1EIHQOJ/yyToULy/nECAwEAAaNFMEMwDgYDVR0P
|
||||
AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFNH1NAz8Uj24
|
||||
PhCGdBkGi0CMQGMLMA0GCSqGSIb3DQEBCwUAA4IBgQBltym8hRgXSAaGTZAciCBc
|
||||
sRtyEkQ584oHUiOmaKvITjnHys/EiETnNaxRw7t/69DKe5g4UaqgdlMwecjJk/Hl
|
||||
jSvXI4mAUERkcIJIEJspMapsEp5QcTAlvskoXjNPFrOW+x0iOLdAM41x5kBDQRkc
|
||||
+N2ie0ITJ5ZX530Ai4ukt76NZNIOio5xoHs1q170kn6xwfS12x1g7CksHlN5Mbw1
|
||||
wtFFeLfQCZVXPNspH7LHJUkrULSTyhleZFJ3ZZqqT9oybpDUhdZB0nZJ6ZC1JiQo
|
||||
2HMwIFV+OsEEG7fNzHhbVKaJmaiOiW2t/CpltebVLSTinz2LmZhzVFRT+y/cdhn3
|
||||
5IsQHzGwEKKtL5XfqJjqWhry+mw/vb+Rze6yy9Li7FkBnetQq8Tb0a2u/UHyzqTA
|
||||
NVhu1wgbRD93vnZqGOkb0gzMRPJC/KibNvFRfaeDXDOiW69Npm/xxXBO/My0CWF1
|
||||
p7cQCkgpkStnWEmm/48WiwGcFWTC2W+mims7JcIpSpc=
|
||||
-----END CERTIFICATE-----
|
25
internal/zero/cmd/testdata/route_downstream_ca_2.crt
vendored
Normal file
25
internal/zero/cmd/testdata/route_downstream_ca_2.crt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEPDCCAqSgAwIBAgIJAPjvgLbEIVj/MA0GCSqGSIb3DQEBCwUAMDoxHjAcBgNV
|
||||
BAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEYMBYGA1UEAxMPZG93bnN0cmVhbSBD
|
||||
QSAyMB4XDTIzMDYwOTAwNDQzOFoXDTMzMDYwOTAwNDQzOFowOjEeMBwGA1UEChMV
|
||||
bWtjZXJ0IGRldmVsb3BtZW50IENBMRgwFgYDVQQDEw9kb3duc3RyZWFtIENBIDIw
|
||||
ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC/8Kog2Zz8e68EGpfiXN7u
|
||||
Xgau38h63ydspucrjhtnSTWXtHO1hYLmUYWAewi79iGYzOuYgWCD3cFxd+tMKLrB
|
||||
yoriJ3KioTtY0pmyLDJ1TXMSaFGgnZqjXHmjMvio0x/jQNkCbYkFBGQSZZvkA8sQ
|
||||
m5AsRDeIUPkPlhFMnb2x4iRcLBP6zDNFfX+y1qSolKbh3K9/E3PT4Unja8gObzCJ
|
||||
nrOcF5SBqTOjRHif/S/wZ9TSFWzLmqGLhq73RahyTiaYP46UvJhrNb5Mo9Hbb94/
|
||||
4zS5B2Zuo4pshSZDWpqwvBecQN0VaLVvIymuSyg5TzuH4ktM0ptzv6rXinDla7rz
|
||||
Mu/FrFVQPksOhTDt5UCSqODwPZiO7g5ST0s+jMpbp1XN8KP2prtElUWdabvHlb0M
|
||||
D2E0hHVi444YkQxZaCoed2obrTB2Df2CwHATgFKvLF1SGS2Q9v0pbUc6Z+0o912b
|
||||
nRfGzi2p7iBsWULuINI3nbNAzlmWPmGiwV1SY1Y0dU8CAwEAAaNFMEMwDgYDVR0P
|
||||
AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAsUNnAWuWM6
|
||||
lc2pqYjQsfCsBRckMA0GCSqGSIb3DQEBCwUAA4IBgQBU6YiRXQ4jkrqugtuLj2a5
|
||||
AQ+URPlfkFFN0BDpWCIzV50w+Y1ZtH2HvGX44zDjbQTwv+AU4T+F75C8Pnc5yvYo
|
||||
v6FIMOOZIrvilokyVf3dKRC3Y2cQac4u64aQk+XR/qjiYoFK0B9yw8UA3O7wA46b
|
||||
ceoZUFZLc5oSsnB9tW72i8lEkBFt2X62rqSQNGYtzCV64bM+ezCsBYPaCIKW0ARB
|
||||
0CbNFGoaPJzAuuGukvOcBDytJ3RJBXJ7l3626KNGxCLsRMcDcTxvXBf7gFWtetW9
|
||||
kuofvlJMiPi3BDMl/FAE5ikj0UR47rjYUxM2SF6F+z8pEcPcePSYzClMECL9a/02
|
||||
I12sEnU3Rf+RpwSTHSCjyXGtWl4dGSJlOElwrYMBAyX62dfFY9GEGgHCnyO1tj39
|
||||
JIhgiIBEZsBL9LOOK8vTYzZ5kBkZ1NXh2Bj3nS/B/M5zotp4/S6P30Li44/Jbpvc
|
||||
70fXruF69zwPMc5b3x7yX7hPLYHk0hm3BOWaodPI4t0=
|
||||
-----END CERTIFICATE-----
|
26
internal/zero/cmd/testdata/tls.crt
vendored
Normal file
26
internal/zero/cmd/testdata/tls.crt
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEUjCCArqgAwIBAgIRAKNaEqCmmZfhmcYgZy01WCswDQYJKoZIhvcNAQELBQAw
|
||||
gYMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTEsMCoGA1UECwwjY2Fs
|
||||
ZWJAY2FsZWItcGMtbGludXggKENhbGViIERveHNleSkxMzAxBgNVBAMMKm1rY2Vy
|
||||
dCBjYWxlYkBjYWxlYi1wYy1saW51eCAoQ2FsZWIgRG94c2V5KTAeFw0yMzExMTAy
|
||||
MDA4NDRaFw0zMzExMDcyMDA4NDRaMFcxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9w
|
||||
bWVudCBjZXJ0aWZpY2F0ZTEsMCoGA1UECwwjY2FsZWJAY2FsZWItcGMtbGludXgg
|
||||
KENhbGViIERveHNleSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8
|
||||
HLBAIzXkPeegldUfRKK2jQxSVZD5g+qsjAzpmrq/AtmweK1cGcOtZ6eOL+p8brPD
|
||||
yVhDT0QlI/O/EKgCOFFxUDqoR82iY06SacAjHni6+PO9tVRbFV0w14BDAJSpB+Vv
|
||||
Wyl+FoPDV/vsZ31FtYw+EwqkbDx/kaT9uzf+LJdlkf14nQQj8Eky/8d3mWJbb/9t
|
||||
jObsaQgJ5LLxCYdImkr77X2LMuDw/1tpH642GE25Nrgm6QHlyKSfYXo38v83ebEq
|
||||
bZUDG+ZioArPmqmkawUWw3ekhj80SJg/TK9PRaN/VvcI1PgAd7LZztUReSmTy5hd
|
||||
9r6rOBxpxwnTDvHkBn6vAgMBAAGjbDBqMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE
|
||||
DDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSFhlhYgEfKTplVOeneVG3+3IE/TTAi
|
||||
BgNVHREEGzAZghcqLmxvY2FsaG9zdC5wb21lcml1bS5pbzANBgkqhkiG9w0BAQsF
|
||||
AAOCAYEApqVzJ3Qf9VqkujFbc0MBDqWD/8gjfd7mW29fRtMIP3zdJliyevRj73AL
|
||||
ifX5ZZunT7n/j52ZziFib4j8uc4R6VwAE7lLpDesfsL4AgvG6ujJaJLh+q6fPFVm
|
||||
8UwIr3/HjZAGPvbwceAO00mtfqn8aK1KeKxfEk9UhTUWhsquby88EcJVhxkTsAHo
|
||||
kKQkEaf9NLazhZ0P0u9J/14VGhMN8QUHvILVjckCDhIj38IUK7UtZHkM72GmKrj2
|
||||
SC40IDdNt4zb1ATLVeyOLdwKjwEFgKWzkvI/7Uj9pA26/eYGPQ7oxRF+IExVIhDr
|
||||
EJvHrWQ0s0EKNPdpU/Ihqtk0rYkj81peqM8TmI6vqrZqAEPza1tYk6WQszDonpPW
|
||||
uKlfr9GYYf5Mu9a2y26AgluDniAcnfWjRXmr1rvRHBpzsLSD3STnPE5t6HJieP7r
|
||||
v6k/flXQ9SEw0U3lI/nZKKwiLfWC2O5BpKwMz19cZ8/kLSJWHg4lkDb2Uo1JKniW
|
||||
+kMEI9nN
|
||||
-----END CERTIFICATE-----
|
28
internal/zero/cmd/testdata/tls.key
vendored
Normal file
28
internal/zero/cmd/testdata/tls.key
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8HLBAIzXkPeeg
|
||||
ldUfRKK2jQxSVZD5g+qsjAzpmrq/AtmweK1cGcOtZ6eOL+p8brPDyVhDT0QlI/O/
|
||||
EKgCOFFxUDqoR82iY06SacAjHni6+PO9tVRbFV0w14BDAJSpB+VvWyl+FoPDV/vs
|
||||
Z31FtYw+EwqkbDx/kaT9uzf+LJdlkf14nQQj8Eky/8d3mWJbb/9tjObsaQgJ5LLx
|
||||
CYdImkr77X2LMuDw/1tpH642GE25Nrgm6QHlyKSfYXo38v83ebEqbZUDG+ZioArP
|
||||
mqmkawUWw3ekhj80SJg/TK9PRaN/VvcI1PgAd7LZztUReSmTy5hd9r6rOBxpxwnT
|
||||
DvHkBn6vAgMBAAECggEAB28i0AYUNSb1JnWFbKzruUctu3tCNXovJg6K3BiPVMkq
|
||||
DT1XrJIgF5RHHOlr3OsLE6u7Xz2ctdML6PshiKTtIwtGpivgRpCiJEslmr2zi8AW
|
||||
8eJeqRLZEfsSSJOXTG7RdGsn4qHFJ00s2ZTlcIHSPwnFm+XjJi99U8G4XsUoXo0r
|
||||
Gy+0VCuU7M8gICEHHsrQO9XDD3nT2jiu5TjrKwjut3EmoJssI5bqx33+OBu5BpCP
|
||||
CT473D43P9p3qi/XnfvqGSG2Oj4OajV4fr0o9B3KvIxkMem7WlI3jyy1kApyXqVT
|
||||
bLkLFyWBNTWUZ2R/2wxmuoC6mLZw879MLCKMvk1doQKBgQDhmwGafJNymTiEQZRI
|
||||
SsQx4seqfOKfgFC7ohqH9cROOu8IJ1o7q2pM2W4XiV+S3wTdPGmca6IOjX23isVB
|
||||
2uqNi9S4MnI2/d22Gd/BR9rvBw1eGJoKbrWx22fE8QCEWT1AnO+DuD0jC85yRls7
|
||||
axzlaMrxEu3LI9UE7NtrdQiByQKBgQDVdI6ceIVBT6RgvVGt8zkLjPIFjhQEHAIp
|
||||
uhirgqpS6CX9Blyf2+o40zmfj3he5rCcEoB5MseM+DgFbcVh2e/MVnYiNNw6JCDB
|
||||
BQkF408pZpSeKXvL/oyV/kImMTJ/tUDY0EXxMwSPJB0WltbWreVIHopigXRCbaey
|
||||
uBHVBv/4twKBgHwHuePy5SU1s2qSmzD7Wc2LPfYu3nCOHNRrFGb26MuRfuReri7r
|
||||
2G8TgoESFycp0QTIN8+1JM0XYKxNcJD6B8V1wKbbpQsymneI1gjutiB/Igw/PkDK
|
||||
CL4VP4F4da5NWW1yWgNygLoJvZ/5qiKKisJc0GWk4HKz6mLgzOjQ2LJxAoGBALHZ
|
||||
fN2YeYbyYcaM11p1VilulVTVjY3i/FZiDR4SL/IGJWjN/Szg4iXYsKFmu+dulOZl
|
||||
cBALpEKrqpmzXYtrN6bsv18+5eO3qGbK2DrEq3eWVev2KoTMobxz7g++XBIWJmLA
|
||||
Hhaa6IiPkYD5yyVyHKDbeXgb3o9eqCR7w7fYLjy/AoGAI4D+MFkivwUF7hqf5edS
|
||||
KrltwmodHiqXNbVkwbW1AFPJbiYai4YFfK4IAbif/Ymxf9G78aOkr9ZpCIzOkDPZ
|
||||
YpEwQGWsAhElCFvc8E/5dHESSp+tWtP+NluimpFqiDg3/SUnMwO2xH0nhLa0zejh
|
||||
gmLh4w/CcPyb9ZyXceWU/nU=
|
||||
-----END PRIVATE KEY-----
|
Loading…
Add table
Add a link
Reference in a new issue