mirror of
https://github.com/pomerium/pomerium.git
synced 2025-05-01 11:26:29 +02:00
This also replaces instances where we manually write "return ctx.Err()" with "return context.Cause(ctx)" which is functionally identical, but will also correctly propagate cause errors if present.
176 lines
4.5 KiB
Go
176 lines
4.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pomerium/pomerium/config"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/pkg/envoy/files"
|
|
"github.com/pomerium/pomerium/pkg/zero/cluster"
|
|
"github.com/pomerium/pomerium/pkg/zero/importutil"
|
|
"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()
|
|
}
|
|
envInfo := findEnvironmentInfo()
|
|
if configFile == "" {
|
|
configFile = envInfo.ConfigArg
|
|
}
|
|
if configFile == "" {
|
|
return fmt.Errorf("no config file provided")
|
|
}
|
|
log.SetLevel(zerolog.ErrorLevel)
|
|
src, err := config.NewFileOrEnvironmentSource(cmd.Context(), configFile, files.FullVersion())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cfg := src.GetConfig()
|
|
|
|
client := zeroClientFromContext(cmd.Context())
|
|
converted := cfg.Options.ToProto()
|
|
for i, name := range importutil.GenerateRouteNames(converted.Routes) {
|
|
converted.Routes[i].Name = name
|
|
}
|
|
var params cluster.ImportConfigurationParams
|
|
if data, err := json.Marshal(envInfo); err == nil {
|
|
hints := make(map[string]string)
|
|
if err := json.Unmarshal(data, &hints); err == nil {
|
|
pairs := []string{}
|
|
for key, value := range hints {
|
|
pairs = append(pairs, fmt.Sprintf("%s=%s", key, value))
|
|
}
|
|
if len(pairs) > 0 {
|
|
params.XImportHints = &pairs
|
|
}
|
|
}
|
|
}
|
|
resp, err := client.ImportConfig(cmd.Context(), converted, ¶ms)
|
|
if err != nil {
|
|
return fmt.Errorf("error importing config: %w", err)
|
|
}
|
|
if resp.Warnings != nil {
|
|
for _, warn := range *resp.Warnings {
|
|
cmd.Printf("warning: %s\n", warn)
|
|
}
|
|
}
|
|
if resp.Messages != nil {
|
|
for _, msg := range *resp.Messages {
|
|
cmd.Printf("✔ %s\n", msg)
|
|
}
|
|
}
|
|
cmd.Println("\nImport successful, return to your browser to continue setup.")
|
|
return nil
|
|
},
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
type environmentInfo struct {
|
|
SystemType string `json:"systemType,omitempty"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
KubernetesNamespace string `json:"kubernetesNamespace,omitempty"`
|
|
Argv0 string `json:"argv0,omitempty"`
|
|
ConfigArg string `json:"configArg,omitempty"`
|
|
}
|
|
|
|
func findEnvironmentInfo() environmentInfo {
|
|
var info environmentInfo
|
|
if isKubernetes() {
|
|
info.SystemType = "kubernetes"
|
|
// search for downward api environment variables to see if we can determine
|
|
// the current namespace (adds '-n <namespace>' to the command given in the
|
|
// zero ui for users to copy/paste)
|
|
for _, env := range []string{
|
|
"POMERIUM_NAMESPACE", // the name we use in our official manifests
|
|
"POD_NAMESPACE", // very common alternative name
|
|
} {
|
|
if v, ok := os.LookupEnv(env); ok {
|
|
info.KubernetesNamespace = v
|
|
break
|
|
}
|
|
}
|
|
} else if isDocker() {
|
|
info.SystemType = "docker"
|
|
} else {
|
|
info.SystemType = "linux"
|
|
info.Argv0 = os.Args[0]
|
|
return info
|
|
}
|
|
info.Hostname, _ = os.Hostname()
|
|
|
|
pid, ok := findPomeriumPid()
|
|
if !ok {
|
|
return info
|
|
}
|
|
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
|
|
if err != nil {
|
|
return info
|
|
}
|
|
args := bytes.Split(cmdline, []byte{0})
|
|
if len(args) > 0 {
|
|
info.Argv0 = string(args[0])
|
|
}
|
|
for i, arg := range args {
|
|
if strings.Contains(string(arg), "-config") {
|
|
if strings.Contains(string(arg), "-config=") {
|
|
info.ConfigArg = strings.Split(string(arg), "=")[1]
|
|
} else if len(args) > i+1 {
|
|
info.ConfigArg = string(args[i+1])
|
|
}
|
|
}
|
|
}
|
|
return info
|
|
}
|
|
|
|
func isKubernetes() bool {
|
|
return os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != ""
|
|
}
|
|
|
|
func isDocker() bool {
|
|
_, err := os.Stat("/.dockerenv")
|
|
return err == nil
|
|
}
|
|
|
|
func findPomeriumPid() (int, bool) {
|
|
pid1Argv0 := getProcessArgv0(1)
|
|
if path.Base(pid1Argv0) == "pomerium" {
|
|
return 1, true
|
|
}
|
|
|
|
pidList, err := os.ReadFile("/proc/1/task/1/children")
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
for _, pidStr := range strings.Fields(string(pidList)) {
|
|
pid, _ := strconv.Atoi(pidStr)
|
|
if path.Base(getProcessArgv0(pid)) == "pomerium" {
|
|
return pid, true
|
|
}
|
|
}
|
|
return 0, false
|
|
}
|
|
|
|
func getProcessArgv0(pid int) string {
|
|
cmdline, err := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
argv0, _, _ := bytes.Cut(cmdline, []byte{0})
|
|
return string(argv0)
|
|
}
|