|
|
|
package nodeconfig
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/base32"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/k3s-io/k3s/pkg/configfilearg"
|
|
|
|
"github.com/k3s-io/k3s/pkg/daemons/config"
|
|
|
|
"github.com/k3s-io/k3s/pkg/version"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
NodeArgsAnnotation = version.Program + ".io/node-args"
|
|
|
|
NodeEnvAnnotation = version.Program + ".io/node-env"
|
|
|
|
NodeConfigHashAnnotation = version.Program + ".io/node-config-hash"
|
|
|
|
ClusterEgressLabel = "egress." + version.Program + ".io/cluster"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
OmittedValue = "********"
|
|
|
|
)
|
|
|
|
|
|
|
|
func getNodeArgs() (string, error) {
|
|
|
|
nodeArgsList := []string{}
|
|
|
|
for _, arg := range configfilearg.MustParse(os.Args[1:]) {
|
|
|
|
if strings.HasPrefix(arg, "--") && strings.Contains(arg, "=") {
|
|
|
|
parsedArg := strings.SplitN(arg, "=", 2)
|
|
|
|
nodeArgsList = append(nodeArgsList, parsedArg...)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
nodeArgsList = append(nodeArgsList, arg)
|
|
|
|
}
|
|
|
|
for i, arg := range nodeArgsList {
|
|
|
|
if isSecret(arg) {
|
|
|
|
if i+1 < len(nodeArgsList) {
|
|
|
|
nodeArgsList[i+1] = OmittedValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
nodeArgs, err := json.Marshal(nodeArgsList)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to retrieve argument list for node")
|
|
|
|
}
|
|
|
|
return string(nodeArgs), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getNodeEnv() (string, error) {
|
|
|
|
k3sEnv := make(map[string]string)
|
|
|
|
for _, v := range os.Environ() {
|
|
|
|
keyValue := strings.SplitN(v, "=", 2)
|
|
|
|
if strings.HasPrefix(keyValue[0], version.ProgramUpper+"_") {
|
|
|
|
k3sEnv[keyValue[0]] = keyValue[1]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for key := range k3sEnv {
|
|
|
|
if isSecret(key) {
|
|
|
|
k3sEnv[key] = OmittedValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
k3sEnvJSON, err := json.Marshal(k3sEnv)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "Failed to retrieve environment map for node")
|
|
|
|
}
|
|
|
|
return string(k3sEnvJSON), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetNodeConfigAnnotations stores a redacted version of the k3s cli args and
|
|
|
|
// environment variables as annotations on the node object. It also stores a
|
|
|
|
// hash of the combined args + variables. These are used by other components
|
|
|
|
// to determine if the node configuration has been changed.
|
|
|
|
func SetNodeConfigAnnotations(nodeConfig *config.Node, node *corev1.Node) (bool, error) {
|
|
|
|
nodeArgs, err := getNodeArgs()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
nodeEnv, err := getNodeEnv()
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
h := sha256.New()
|
|
|
|
_, err = h.Write([]byte(nodeArgs + nodeEnv))
|
|
|
|
if err != nil {
|
|
|
|
return false, fmt.Errorf("Failed to hash the node config: %v", err)
|
|
|
|
}
|
|
|
|
if node.Annotations == nil {
|
|
|
|
node.Annotations = make(map[string]string)
|
|
|
|
}
|
|
|
|
configHash := h.Sum(nil)
|
|
|
|
encoded := base32.StdEncoding.EncodeToString(configHash[:])
|
|
|
|
if node.Annotations[NodeConfigHashAnnotation] == encoded {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
node.Annotations[NodeEnvAnnotation] = nodeEnv
|
|
|
|
node.Annotations[NodeArgsAnnotation] = nodeArgs
|
|
|
|
node.Annotations[NodeConfigHashAnnotation] = encoded
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetNodeConfigLabels adds labels for functionality flags
|
|
|
|
// that may not be present on down-level or up-level nodes.
|
|
|
|
// These labels are used by other components to determine whether
|
|
|
|
// or not a node supports particular functionality.
|
|
|
|
func SetNodeConfigLabels(nodeConfig *config.Node, node *corev1.Node) (bool, error) {
|
|
|
|
if node.Labels == nil {
|
|
|
|
node.Labels = make(map[string]string)
|
|
|
|
}
|
|
|
|
_, hasLabel := node.Labels[ClusterEgressLabel]
|
|
|
|
switch nodeConfig.EgressSelectorMode {
|
|
|
|
case config.EgressSelectorModeCluster, config.EgressSelectorModePod:
|
|
|
|
if !hasLabel {
|
|
|
|
node.Labels[ClusterEgressLabel] = "true"
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if hasLabel {
|
|
|
|
delete(node.Labels, ClusterEgressLabel)
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isSecret(key string) bool {
|
|
|
|
secretData := []string{
|
|
|
|
version.ProgramUpper + "_TOKEN",
|
|
|
|
version.ProgramUpper + "_DATASTORE_ENDPOINT",
|
|
|
|
version.ProgramUpper + "_AGENT_TOKEN",
|
|
|
|
version.ProgramUpper + "_CLUSTER_SECRET",
|
|
|
|
version.ProgramUpper + "_VPN_AUTH",
|
|
|
|
"AWS_ACCESS_KEY_ID",
|
|
|
|
"AWS_SECRET_ACCESS_KEY",
|
|
|
|
"--token",
|
|
|
|
"-t",
|
|
|
|
"--agent-token",
|
|
|
|
"--datastore-endpoint",
|
|
|
|
"--etcd-s3-access-key",
|
|
|
|
"--etcd-s3-secret-key",
|
|
|
|
"--vpn-auth",
|
|
|
|
}
|
|
|
|
for _, secret := range secretData {
|
|
|
|
if key == secret {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|