|  | 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) | 
|  | } |