| package main |
| |
| import ( |
| "context" |
| "crypto/ed25519" |
| "encoding/pem" |
| "log" |
| "net" |
| "os" |
| "path/filepath" |
| |
| "github.com/adrg/xdg" |
| "github.com/spf13/cobra" |
| |
| 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) |
| |
| client, err := rpc.NewEphemeralClient(net.JoinHostPort(args[0], node.CuratorServicePort.PortString()), ownerPrivateKey, nil) |
| 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) |
| } |