blob: 503248ac8c948efd495c7f722387eea3aba4d367 [file] [log] [blame]
Lorenz Bruna9b455f2021-12-07 03:53:22 +01001package main
2
3import (
4 "context"
5 "crypto/ed25519"
6 "encoding/pem"
7 "log"
8 "net"
9 "os"
10 "path/filepath"
11
12 "github.com/adrg/xdg"
13 "github.com/spf13/cobra"
14
Lorenz Bruna9b455f2021-12-07 03:53:22 +010015 clicontext "source.monogon.dev/metropolis/cli/pkg/context"
16 "source.monogon.dev/metropolis/node"
17 "source.monogon.dev/metropolis/node/core/rpc"
Serge Bazanskidc1bec42021-12-16 17:38:53 +010018 apb "source.monogon.dev/metropolis/proto/api"
Lorenz Bruna9b455f2021-12-07 03:53:22 +010019)
20
21var takeownershipCommand = &cobra.Command{
22 Use: "takeownership <node-addr>",
23 Short: "Takes ownership of a new Metropolis cluster",
24 Long: `This takes ownership of a new Metropolis cluster by asking the new
25cluster to issue an owner certificate to for the owner key generated by a
26previous invocation of metroctl install on this machine.`,
27 Example: "takeownership 192.0.2.1",
28 Args: cobra.ExactArgs(1), // One positional argument: the node address
29 Run: doTakeOwnership,
30}
31
32func doTakeOwnership(cmd *cobra.Command, args []string) {
33 ctx := clicontext.WithInterrupt(context.Background())
34 ownerPrivateKeyPEM, err := os.ReadFile(filepath.Join(xdg.ConfigHome, "metroctl/owner-key.pem"))
35 if os.IsNotExist(err) {
36 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.")
37 } else if err != nil {
38 log.Fatalf("Failed to load owner private key: %v", err)
39 }
40 block, _ := pem.Decode(ownerPrivateKeyPEM)
41 if block == nil {
42 log.Fatalf("owner-key.pem contains invalid PEM")
43 }
44 if block.Type != ownerKeyType {
45 log.Fatalf("owner-key.pem contains a PEM block that's not a %v", ownerKeyType)
46 }
47 if len(block.Bytes) != ed25519.PrivateKeySize {
48 log.Fatal("owner-key.pem contains non-Ed25519 key")
49 }
50 ownerPrivateKey := ed25519.PrivateKey(block.Bytes)
51
52 client, err := rpc.NewEphemeralClient(net.JoinHostPort(args[0], node.CuratorServicePort.PortString()), ownerPrivateKey, nil)
53 if err != nil {
54 log.Fatalf("Failed to create client to given node address: %v", err)
55 }
56 defer client.Close()
57 aaa := apb.NewAAAClient(client)
58 ownerCert, err := rpc.RetrieveOwnerCertificate(ctx, aaa, ownerPrivateKey)
59 if err != nil {
60 log.Fatalf("Failed to retrive owner certificate from cluster: %v", err)
61 }
62 ownerCertPEM := pem.Block{
63 Type: "CERTIFICATE",
64 Bytes: ownerCert.Certificate[0],
65 }
66 if err := os.WriteFile(filepath.Join(xdg.ConfigHome, "metroctl/owner.pem"), pem.EncodeToMemory(&ownerCertPEM), 0644); err != nil {
67 log.Printf("Failed to store retrieved owner certificate: %v", err)
68 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.")
69 }
70 log.Print("Successfully retrieved owner credentials! You now own this cluster.")
71}
72
73func init() {
74 rootCmd.AddCommand(takeownershipCommand)
75}