Skip to content

Commit

Permalink
Allow passing in kubeconfig contents during runtime (#275)
Browse files Browse the repository at this point in the history
e.g. for receptorctl, --param secret_kube_config="apiVersion: v1..."

if the value for --param starts with @, it is interpreted as a file. In this case the file is read and the contents are passed to receptor

e.g. --param secret_kube_config=@/home/sfoster/.kube/config

Note: the @ indicating a filename works for any --param, not just secrete_kube_config
  • Loading branch information
fosterseth authored Dec 11, 2020
1 parent d80736f commit e293394
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 68 deletions.
90 changes: 23 additions & 67 deletions pkg/workceptor/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,14 @@ type kubeUnit struct {

// kubeExtraData is the content of the ExtraData JSON field for a Kubernetes worker
type kubeExtraData struct {
Image string
Command string
Params string
KubeHost string
KubeAPIPath string
KubeNamespace string
KubeUsername string
KubePassword string
KubeBearerToken string
KubeVerifyTLS bool
KubeTLSCAData string
PodName string
Image string
Command string
Params string
KubeNamespace string
KubeVerifyTLS bool
KubeTLSCAData string
KubeConfig string
PodName string
}

// ErrPodCompleted is returned when pod has already completed before we could attach
Expand Down Expand Up @@ -512,7 +508,7 @@ func (kw *kubeUnit) connectUsingKubeconfig() error {
if kw.kubeConfig != "" {
clr.ExplicitPath = kw.kubeConfig
}
ked := kw.Status().ExtraData.(*kubeExtraData)
ked := kw.UnredactedStatus().ExtraData.(*kubeExtraData)
if ked.KubeNamespace == "" {
c, err := clr.Load()
if err != nil {
Expand All @@ -522,7 +518,11 @@ func (kw *kubeUnit) connectUsingKubeconfig() error {
sfd.ExtraData.(*kubeExtraData).KubeNamespace = c.Contexts[c.CurrentContext].Namespace
})
}
kw.config, err = clientcmd.BuildConfigFromFlags("", clr.GetDefaultFilename())
if ked.KubeConfig != "" {
kw.config, err = clientcmd.RESTConfigFromKubeConfig([]byte(ked.KubeConfig))
} else {
kw.config, err = clientcmd.BuildConfigFromFlags("", clr.GetDefaultFilename())
}
if err != nil {
return err
}
Expand All @@ -538,32 +538,12 @@ func (kw *kubeUnit) connectUsingIncluster() error {
return nil
}

func (kw *kubeUnit) connectUsingParams() error {
ked := kw.UnredactedStatus().ExtraData.(*kubeExtraData)
kw.config = &rest.Config{
Host: ked.KubeHost,
APIPath: ked.KubeAPIPath,
Username: ked.KubeUsername,
Password: ked.KubePassword,
BearerToken: ked.KubeBearerToken,
TLSClientConfig: rest.TLSClientConfig{
Insecure: !ked.KubeVerifyTLS,
},
}
if ked.KubeTLSCAData != "" {
kw.config.TLSClientConfig.CAData = []byte(ked.KubeTLSCAData)
}
return nil
}

func (kw *kubeUnit) connectToKube() error {
var err error
if kw.authMethod == "kubeconfig" {
err = kw.connectUsingKubeconfig()
} else if kw.authMethod == "incluster" {
err = kw.connectUsingIncluster()
} else if kw.authMethod == "params" {
err = kw.connectUsingParams()
} else {
return fmt.Errorf("unknown auth method %s", kw.authMethod)
}
Expand Down Expand Up @@ -608,13 +588,8 @@ func (kw *kubeUnit) SetFromParams(params map[string]string) error {
{name: "kube_command", permission: kw.allowRuntimeCommand, setter: setString(&ked.Command)},
{name: "kube_image", permission: kw.allowRuntimeCommand, setter: setString(&ked.Image)},
{name: "kube_params", permission: kw.allowRuntimeParams, setter: setString(&userParams)},
{name: "kube_config", permission: kw.allowRuntimeAuth, setter: setString(&kw.kubeConfig)},
{name: "kube_namespace", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeNamespace)},
{name: "kube_host", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeHost)},
{name: "kube_api_path", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeAPIPath)},
{name: "kube_username", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeUsername)},
{name: "secret_kube_password", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubePassword)},
{name: "secret_kube_bearer_token", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeBearerToken)},
{name: "secret_kube_config", permission: kw.allowRuntimeAuth, setter: setString(&ked.KubeConfig)},
{name: "kube_verify_tls", permission: kw.allowRuntimeTLS, setter: setBool(&ked.KubeVerifyTLS)},
{name: "kube_tls_ca", permission: kw.allowRuntimeTLS, setter: setString(&ked.KubeTLSCAData)},
}
Expand All @@ -640,8 +615,7 @@ func (kw *kubeUnit) Status() *StatusFileData {
status := kw.UnredactedStatus()
ed, ok := status.ExtraData.(*kubeExtraData)
if ok {
ed.KubePassword = ""
ed.KubeBearerToken = ""
ed.KubeConfig = ""
}
return status
}
Expand Down Expand Up @@ -747,13 +721,8 @@ type WorkKubeCfg struct {
Image string `description:"Container image to use for the worker pod"`
Command string `description:"Command to run in the container (overrides entrypoint)"`
Params string `description:"Command-line parameters to pass to the entrypoint"`
AuthMethod string `description:"One of: kubeconfig, incluster, params" default:"incluster"`
AuthMethod string `description:"One of: kubeconfig, incluster" default:"incluster"`
KubeConfig string `description:"Kubeconfig filename (for authmethod=kubeconfig)"`
KubeHost string `description:"k8s API hostname (for authmethod=params)"`
KubeAPIPath string `description:"k8s API path (for authmethod=params)"`
KubeUsername string `description:"k8s API username (for authmethod=params)"`
KubePassword string `description:"k8s API password (for authmethod=params)"`
KubeBearerToken string `description:"k8s API bearer token (for authmethod=params)"`
KubeVerifyTLS bool `description:"verify server TLS certificate/hostname" default:"true"`
KubeTLSCAData string `description:"CA certificate PEM data to verify against"`
AllowRuntimeAuth bool `description:"Allow passing API parameters at runtime" default:"false"`
Expand All @@ -770,16 +739,11 @@ func (cfg WorkKubeCfg) newWorker(w *Workceptor, unitID string, workType string)
BaseWorkUnit: BaseWorkUnit{
status: StatusFileData{
ExtraData: &kubeExtraData{
Image: cfg.Image,
Command: cfg.Command,
KubeHost: cfg.KubeHost,
KubeAPIPath: cfg.KubeAPIPath,
KubeNamespace: cfg.Namespace,
KubeUsername: cfg.KubeUsername,
KubePassword: cfg.KubePassword,
KubeBearerToken: cfg.KubeBearerToken,
KubeVerifyTLS: cfg.KubeVerifyTLS,
KubeTLSCAData: cfg.KubeTLSCAData,
Image: cfg.Image,
Command: cfg.Command,
KubeNamespace: cfg.Namespace,
KubeVerifyTLS: cfg.KubeVerifyTLS,
KubeTLSCAData: cfg.KubeTLSCAData,
},
},
},
Expand All @@ -801,7 +765,7 @@ func (cfg WorkKubeCfg) newWorker(w *Workceptor, unitID string, workType string)
// Prepare inspects the configuration for validity
func (cfg WorkKubeCfg) Prepare() error {
lcAuth := strings.ToLower(cfg.AuthMethod)
if lcAuth != "kubeconfig" && lcAuth != "incluster" && lcAuth != "params" {
if lcAuth != "kubeconfig" && lcAuth != "incluster" {
return fmt.Errorf("invalid AuthMethod: %s", cfg.AuthMethod)
}
if cfg.Namespace == "" && !(lcAuth == "kubeconfig" || cfg.AllowRuntimeAuth) {
Expand All @@ -816,14 +780,6 @@ func (cfg WorkKubeCfg) Prepare() error {
return fmt.Errorf("error accessing kubeconfig file: %s", err)
}
}
if lcAuth == "params" && !cfg.AllowRuntimeAuth {
if cfg.KubeHost == "" {
return fmt.Errorf("when AuthMethod=params, must provide KubeHost")
}
if (cfg.KubeUsername == "" || cfg.KubePassword == "") && cfg.KubeBearerToken == "" {
return fmt.Errorf("when AuthMethod=params, must provide either KubeBearerToken or KubeUsername and KubePassword")
}
}
if cfg.KubeTLSCAData != "" {
block, _ := pem.Decode([]byte(cfg.KubeTLSCAData))
if block == nil || block.Type != "BEGIN CERTIFICATE" {
Expand Down
13 changes: 12 additions & 1 deletion receptorctl/receptorctl/socket_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,18 @@ def submit_work(self, worktype, payload, node=None, tlsclient=None, ttl=None, pa
if params:
for k,v in params.items():
if k not in commandMap:
commandMap[k] = v
if v[0] == '@' and v[:2] != '@@':
fname = v[1:]
if not os.path.exists(fname):
raise FileNotFoundError("{} does not exist".format(fname))
try:
with open(fname, 'r') as f:
v_contents = f.read()
except:
raise OSError("could not read from file {}".format(fname))
commandMap[k] = v_contents
else:
commandMap[k] = v
else:
raise RuntimeError(f"Duplicate or illegal parameter {k}")
commandJson = json.dumps(commandMap)
Expand Down

0 comments on commit e293394

Please sign in to comment.