...

Source file src/edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/clustersecrets/breakglass/breakglass.go

Documentation: edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/plugins/clustersecrets/breakglass

     1  package breakglass
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	corev1 "k8s.io/api/core/v1"
     9  
    10  	"edge-infra.dev/pkg/edge/api/graph/model"
    11  	"edge-infra.dev/pkg/k8s/runtime/controller/reconcile"
    12  	"edge-infra.dev/pkg/sds/clustersecrets"
    13  	"edge-infra.dev/pkg/sds/clustersecrets/audit"
    14  	"edge-infra.dev/pkg/sds/clustersecrets/breakglass"
    15  	v1ien "edge-infra.dev/pkg/sds/ien/k8s/apis/v1"
    16  	"edge-infra.dev/pkg/sds/ien/k8s/controllers/nodeagent/config"
    17  	"edge-infra.dev/pkg/sds/ien/node"
    18  )
    19  
    20  type Plugin struct{}
    21  
    22  var (
    23  	readOnlyShadowFilePath = "/host-rofs/etc/shadow" // path to read-only shadow file
    24  	shadowFilePath         = "/host-etc/shadow"      // path to shadow file to update
    25  )
    26  
    27  func (plugin Plugin) Reconcile(ctx context.Context, ienode *v1ien.IENode, cfg config.Config) (reconcile.Result, error) {
    28  	secret, err := breakglass.FromClient(ctx, cfg.GetClient())
    29  	if err != nil {
    30  		return reconcile.ResultRequeue, err
    31  	}
    32  	if err := apply(secret, ienode, cfg); err != nil {
    33  		return reconcile.ResultRequeue, err
    34  	}
    35  	return reconcile.ResultSuccess, nil
    36  }
    37  
    38  func apply(breakglassSecret *corev1.Secret, ienode *v1ien.IENode, cfg config.Config) error {
    39  	secret, err := breakglass.FromSecret(breakglassSecret)
    40  	if err != nil {
    41  		return fmt.Errorf("error reading breakglass cluster secret: %w", err)
    42  	}
    43  	shadowFileEntry := secret.String()
    44  	changed, err := write(shadowFileEntry, secret.Version())
    45  	if err != nil {
    46  		return fmt.Errorf("error applying breakglass secret: %w", err)
    47  	}
    48  	if !changed {
    49  		return nil
    50  	}
    51  
    52  	terminalID := ienode.ObjectMeta.Labels[node.TerminalIDLabel]
    53  	// status reporting
    54  	if ienode.Status.ClusterSecrets == nil {
    55  		ienode.Status.ClusterSecrets = &v1ien.ClusterSecretInventory{}
    56  	}
    57  	ienode.Status.ClusterSecrets.AddClusterSecret(breakglass.HashedSecretName, secret.Version(), terminalID)
    58  
    59  	eventRecorder := cfg.GetEventRecorder()
    60  	clustersecrets.RecordClusterSecretEvent(eventRecorder, breakglassSecret, secret, terminalID)
    61  	return nil
    62  }
    63  
    64  // write will take a shadow file entry and attempt to update the shadow file.
    65  // This is a safe write so the file is temporarily written to tmp directory and the
    66  // entire file is copied to /etc/shadow.
    67  func write(shadowFileEntry, version string) (bool, error) {
    68  	baseFile, err := os.ReadFile(readOnlyShadowFilePath)
    69  	if err != nil {
    70  		return false, err
    71  	}
    72  
    73  	currentContents, err := os.ReadFile(shadowFilePath)
    74  	if err != nil {
    75  		return false, err
    76  	}
    77  
    78  	hasChanged, contents := breakglass.UpsertEntry(currentContents, baseFile, shadowFileEntry)
    79  	if !hasChanged {
    80  		return false, nil
    81  	}
    82  
    83  	// audit log indicating cluster secret is taking effect
    84  	auditLog := audit.New("nodeagent")
    85  	auditLog.Log(model.ClusterSecretTypeBreakglass, audit.SecretApplied, "action_by", "nodeagent", "version", version)
    86  
    87  	shadowFileInfo, err := os.Stat(shadowFilePath)
    88  	if err != nil {
    89  		return false, fmt.Errorf("error stat on file: %v", err)
    90  	}
    91  
    92  	file, err := os.OpenFile(shadowFilePath, os.O_WRONLY|os.O_TRUNC, shadowFileInfo.Mode())
    93  	if err != nil {
    94  		return false, fmt.Errorf("error opening file: %v", err)
    95  	}
    96  	defer file.Close()
    97  	_, err = file.Write(contents)
    98  	return err == nil, err
    99  }
   100  

View as plain text