| package main | 
 |  | 
 | import ( | 
 | 	"context" | 
 | 	"crypto/ed25519" | 
 | 	"encoding/pem" | 
 | 	"log" | 
 | 	"net" | 
 | 	"os" | 
 | 	"path/filepath" | 
 |  | 
 | 	"github.com/adrg/xdg" | 
 | 	"github.com/spf13/cobra" | 
 | 	"google.golang.org/grpc" | 
 |  | 
 | 	clicontext "source.monogon.dev/metropolis/cli/pkg/context" | 
 | 	"source.monogon.dev/metropolis/node" | 
 | 	"source.monogon.dev/metropolis/node/core/rpc" | 
 | 	apb "source.monogon.dev/metropolis/proto/api" | 
 | ) | 
 |  | 
 | var takeownershipCommand = &cobra.Command{ | 
 | 	Use:   "takeownership <node-addr>", | 
 | 	Short: "Takes ownership of a new Metropolis cluster", | 
 | 	Long: `This takes ownership of a new Metropolis cluster by asking the new | 
 | cluster to issue an owner certificate to for the owner key generated by a | 
 | previous invocation of metroctl install on this machine.`, | 
 | 	Example: "takeownership 192.0.2.1", | 
 | 	Args:    cobra.ExactArgs(1), // One positional argument: the node address | 
 | 	Run:     doTakeOwnership, | 
 | } | 
 |  | 
 | func doTakeOwnership(cmd *cobra.Command, args []string) { | 
 | 	ctx := clicontext.WithInterrupt(context.Background()) | 
 | 	ownerPrivateKeyPEM, err := os.ReadFile(filepath.Join(xdg.ConfigHome, "metroctl/owner-key.pem")) | 
 | 	if os.IsNotExist(err) { | 
 | 		log.Fatalf("Owner key does not exist. takeownership needs to be executed on the same system that has previously installed the cluster using metroctl install.") | 
 | 	} else if err != nil { | 
 | 		log.Fatalf("Failed to load owner private key: %v", err) | 
 | 	} | 
 | 	block, _ := pem.Decode(ownerPrivateKeyPEM) | 
 | 	if block == nil { | 
 | 		log.Fatalf("owner-key.pem contains invalid PEM") | 
 | 	} | 
 | 	if block.Type != ownerKeyType { | 
 | 		log.Fatalf("owner-key.pem contains a PEM block that's not a %v", ownerKeyType) | 
 | 	} | 
 | 	if len(block.Bytes) != ed25519.PrivateKeySize { | 
 | 		log.Fatal("owner-key.pem contains non-Ed25519 key") | 
 | 	} | 
 | 	ownerPrivateKey := ed25519.PrivateKey(block.Bytes) | 
 |  | 
 | 	ephCreds, err := rpc.NewEphemeralCredentials(ownerPrivateKey, nil) | 
 | 	if err != nil { | 
 | 		log.Fatalf("Failed to create ephemeral credentials: %v", err) | 
 | 	} | 
 | 	client, err := grpc.Dial(net.JoinHostPort(args[0], node.CuratorServicePort.PortString()), grpc.WithTransportCredentials(ephCreds)) | 
 | 	if err != nil { | 
 | 		log.Fatalf("Failed to create client to given node address: %v", err) | 
 | 	} | 
 | 	defer client.Close() | 
 | 	aaa := apb.NewAAAClient(client) | 
 | 	ownerCert, err := rpc.RetrieveOwnerCertificate(ctx, aaa, ownerPrivateKey) | 
 | 	if err != nil { | 
 | 		log.Fatalf("Failed to retrive owner certificate from cluster: %v", err) | 
 | 	} | 
 | 	ownerCertPEM := pem.Block{ | 
 | 		Type:  "CERTIFICATE", | 
 | 		Bytes: ownerCert.Certificate[0], | 
 | 	} | 
 | 	if err := os.WriteFile(filepath.Join(xdg.ConfigHome, "metroctl/owner.pem"), pem.EncodeToMemory(&ownerCertPEM), 0644); err != nil { | 
 | 		log.Printf("Failed to store retrieved owner certificate: %v", err) | 
 | 		log.Fatalln("Sorry, the cluster has been lost as taking ownership cannot be repeated. Fix the reason the file couldn't be written and reinstall the node.") | 
 | 	} | 
 | 	log.Print("Successfully retrieved owner credentials! You now own this cluster.") | 
 | } | 
 |  | 
 | func init() { | 
 | 	rootCmd.AddCommand(takeownershipCommand) | 
 | } |