github.com/argoproj/argo-cd/v3@v3.2.1/cmd/argocd-dex/commands/argocd_dex.go (about) 1 package commands 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "os/exec" 8 "runtime/debug" 9 "syscall" 10 11 "github.com/argoproj/argo-cd/v3/common" 12 13 log "github.com/sirupsen/logrus" 14 "github.com/spf13/cobra" 15 "k8s.io/client-go/kubernetes" 16 "k8s.io/client-go/tools/clientcmd" 17 "sigs.k8s.io/yaml" 18 19 cmdutil "github.com/argoproj/argo-cd/v3/cmd/util" 20 "github.com/argoproj/argo-cd/v3/util/cli" 21 "github.com/argoproj/argo-cd/v3/util/dex" 22 "github.com/argoproj/argo-cd/v3/util/env" 23 "github.com/argoproj/argo-cd/v3/util/errors" 24 "github.com/argoproj/argo-cd/v3/util/settings" 25 "github.com/argoproj/argo-cd/v3/util/tls" 26 ) 27 28 const ( 29 cliName = "argocd-dex" 30 ) 31 32 func NewCommand() *cobra.Command { 33 command := &cobra.Command{ 34 Use: cliName, 35 Short: "argocd-dex tools used by Argo CD", 36 Long: "argocd-dex has internal utility tools used by Argo CD", 37 DisableAutoGenTag: true, 38 Run: func(c *cobra.Command, args []string) { 39 c.HelpFunc()(c, args) 40 }, 41 } 42 43 command.AddCommand(NewRunDexCommand()) 44 command.AddCommand(NewGenDexConfigCommand()) 45 return command 46 } 47 48 func NewRunDexCommand() *cobra.Command { 49 var ( 50 clientConfig clientcmd.ClientConfig 51 disableTLS bool 52 ) 53 command := cobra.Command{ 54 Use: "rundex", 55 Short: "Runs dex generating a config using settings from the Argo CD configmap and secret", 56 RunE: func(c *cobra.Command, _ []string) error { 57 ctx := c.Context() 58 59 vers := common.GetVersion() 60 namespace, _, err := clientConfig.Namespace() 61 errors.CheckError(err) 62 vers.LogStartupInfo( 63 "ArgoCD Dex Server", 64 map[string]any{ 65 "namespace": namespace, 66 }, 67 ) 68 69 cli.SetLogFormat(cmdutil.LogFormat) 70 cli.SetLogLevel(cmdutil.LogLevel) 71 72 // Recover from panic and log the error using the configured logger instead of the default. 73 defer func() { 74 if r := recover(); r != nil { 75 log.WithField("trace", string(debug.Stack())).Fatal("Recovered from panic: ", r) 76 } 77 }() 78 79 _, err = exec.LookPath("dex") 80 errors.CheckError(err) 81 config, err := clientConfig.ClientConfig() 82 errors.CheckError(err) 83 config.UserAgent = fmt.Sprintf("argocd-dex/%s (%s)", vers.Version, vers.Platform) 84 kubeClientset := kubernetes.NewForConfigOrDie(config) 85 86 if !disableTLS { 87 config, err := tls.CreateServerTLSConfig("/tls/tls.crt", "/tls/tls.key", []string{"localhost", "dexserver"}) 88 if err != nil { 89 log.Fatalf("could not create TLS config: %v", err) 90 } 91 certPem, keyPem := tls.EncodeX509KeyPair(config.Certificates[0]) 92 err = os.WriteFile("/tmp/tls.crt", certPem, 0o600) 93 if err != nil { 94 log.Fatalf("could not write TLS certificate: %v", err) 95 } 96 err = os.WriteFile("/tmp/tls.key", keyPem, 0o600) 97 if err != nil { 98 log.Fatalf("could not write TLS key: %v", err) 99 } 100 } 101 102 settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace) 103 prevSettings, err := settingsMgr.GetSettings() 104 errors.CheckError(err) 105 updateCh := make(chan *settings.ArgoCDSettings, 1) 106 settingsMgr.Subscribe(updateCh) 107 108 for { 109 var cmd *exec.Cmd 110 dexCfgBytes, err := dex.GenerateDexConfigYAML(prevSettings, disableTLS) 111 errors.CheckError(err) 112 if len(dexCfgBytes) == 0 { 113 log.Infof("dex is not configured") 114 } else { 115 err = os.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0o644) 116 errors.CheckError(err) 117 log.Debug(redactor(string(dexCfgBytes))) 118 cmd = exec.Command("dex", "serve", "/tmp/dex.yaml") 119 cmd.Stdout = os.Stdout 120 cmd.Stderr = os.Stderr 121 err = cmd.Start() 122 errors.CheckError(err) 123 } 124 125 // loop until the dex config changes 126 for { 127 newSettings := <-updateCh 128 newDexCfgBytes, err := dex.GenerateDexConfigYAML(newSettings, disableTLS) 129 errors.CheckError(err) 130 if !bytes.Equal(newDexCfgBytes, dexCfgBytes) { 131 prevSettings = newSettings 132 log.Infof("dex config modified. restarting dex") 133 if cmd != nil && cmd.Process != nil { 134 err = cmd.Process.Signal(syscall.SIGTERM) 135 errors.CheckError(err) 136 _, err = cmd.Process.Wait() 137 errors.CheckError(err) 138 } 139 break 140 } 141 log.Infof("dex config unmodified") 142 } 143 } 144 }, 145 } 146 147 clientConfig = cli.AddKubectlFlagsToCmd(&command) 148 command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "json"), "Set the logging format. One of: json|text") 149 command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") 150 command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint") 151 return &command 152 } 153 154 func NewGenDexConfigCommand() *cobra.Command { 155 var ( 156 clientConfig clientcmd.ClientConfig 157 out string 158 disableTLS bool 159 ) 160 command := cobra.Command{ 161 Use: "gendexcfg", 162 Short: "Generates a dex config from Argo CD settings", 163 RunE: func(c *cobra.Command, _ []string) error { 164 ctx := c.Context() 165 166 cli.SetLogFormat(cmdutil.LogFormat) 167 cli.SetLogLevel(cmdutil.LogLevel) 168 169 config, err := clientConfig.ClientConfig() 170 errors.CheckError(err) 171 namespace, _, err := clientConfig.Namespace() 172 errors.CheckError(err) 173 kubeClientset := kubernetes.NewForConfigOrDie(config) 174 settingsMgr := settings.NewSettingsManager(ctx, kubeClientset, namespace) 175 settings, err := settingsMgr.GetSettings() 176 errors.CheckError(err) 177 dexCfgBytes, err := dex.GenerateDexConfigYAML(settings, disableTLS) 178 errors.CheckError(err) 179 if len(dexCfgBytes) == 0 { 180 log.Infof("dex is not configured") 181 return nil 182 } 183 if out == "" { 184 dexCfg := make(map[string]any) 185 err := yaml.Unmarshal(dexCfgBytes, &dexCfg) 186 errors.CheckError(err) 187 if staticClientsInterface, ok := dexCfg["staticClients"]; ok { 188 if staticClients, ok := staticClientsInterface.([]any); ok { 189 for i := range staticClients { 190 staticClient := staticClients[i] 191 if mappings, ok := staticClient.(map[string]any); ok { 192 for key := range mappings { 193 if key == "secret" { 194 mappings[key] = "******" 195 } 196 } 197 staticClients[i] = mappings 198 } 199 } 200 dexCfg["staticClients"] = staticClients 201 } 202 } 203 errors.CheckError(err) 204 maskedDexCfgBytes, err := yaml.Marshal(dexCfg) 205 errors.CheckError(err) 206 fmt.Print(string(maskedDexCfgBytes)) 207 } else { 208 err = os.WriteFile(out, dexCfgBytes, 0o644) 209 errors.CheckError(err) 210 } 211 return nil 212 }, 213 } 214 215 clientConfig = cli.AddKubectlFlagsToCmd(&command) 216 command.Flags().StringVar(&cmdutil.LogFormat, "logformat", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGFORMAT", "json"), "Set the logging format. One of: json|text") 217 command.Flags().StringVar(&cmdutil.LogLevel, "loglevel", env.StringFromEnv("ARGOCD_DEX_SERVER_LOGLEVEL", "info"), "Set the logging level. One of: debug|info|warn|error") 218 command.Flags().StringVarP(&out, "out", "o", "", "Output to the specified file instead of stdout") 219 command.Flags().BoolVar(&disableTLS, "disable-tls", env.ParseBoolFromEnv("ARGOCD_DEX_SERVER_DISABLE_TLS", false), "Disable TLS on the HTTP endpoint") 220 return &command 221 } 222 223 func iterateStringFields(obj any, callback func(name string, val string) string) { 224 switch obj := obj.(type) { 225 case map[string]any: 226 for field, val := range obj { 227 if strVal, ok := val.(string); ok { 228 obj[field] = callback(field, strVal) 229 } else { 230 iterateStringFields(val, callback) 231 } 232 } 233 case []any: 234 for i := range obj { 235 iterateStringFields(obj[i], callback) 236 } 237 } 238 } 239 240 func redactor(dirtyString string) string { 241 config := make(map[string]any) 242 err := yaml.Unmarshal([]byte(dirtyString), &config) 243 errors.CheckError(err) 244 iterateStringFields(config, func(name string, val string) string { 245 if name == "clientSecret" || name == "secret" || name == "bindPW" { 246 return "********" 247 } 248 return val 249 }) 250 data, err := yaml.Marshal(config) 251 errors.CheckError(err) 252 return string(data) 253 }