blob: a34e470c0982888535dfcceb03c5a96a46557783 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brune6573102021-11-02 14:15:37 +01004package main
5
6import (
Serge Bazanski97783222021-12-14 16:04:26 +01007 "bytes"
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +02008 "context"
Lorenz Brune6573102021-11-02 14:15:37 +01009 "crypto/ed25519"
Lorenz Brun7a510192022-07-04 15:31:38 +000010 _ "embed"
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020011 "fmt"
Lorenz Brune6573102021-11-02 14:15:37 +010012 "os"
Tim Windelschmidtb765f242024-05-08 01:40:02 +020013 "os/signal"
Lorenz Brune6573102021-11-02 14:15:37 +010014
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010015 "github.com/bazelbuild/rules_go/go/runfiles"
Lorenz Brune6573102021-11-02 14:15:37 +010016 "github.com/spf13/cobra"
Timon Stampflib9701c32024-12-15 17:50:01 +010017 "google.golang.org/protobuf/encoding/prototext"
Lorenz Brune6573102021-11-02 14:15:37 +010018
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010019 "source.monogon.dev/metropolis/proto/api"
20 cpb "source.monogon.dev/metropolis/proto/common"
21
Jan Schära9b060b2024-08-07 10:42:29 +020022 "source.monogon.dev/metropolis/cli/flagdefs"
Lorenz Brune6573102021-11-02 14:15:37 +010023 "source.monogon.dev/metropolis/cli/metroctl/core"
Jan Schär39f4f5c2024-10-29 09:41:50 +010024 common "source.monogon.dev/metropolis/node"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020025 "source.monogon.dev/osbase/blkio"
26 "source.monogon.dev/osbase/fat32"
Lorenz Brune6573102021-11-02 14:15:37 +010027)
28
29var installCmd = &cobra.Command{
Lorenz Brun7a510192022-07-04 15:31:38 +000030 Short: "Contains subcommands to install Metropolis via different media.",
Lorenz Brune6573102021-11-02 14:15:37 +010031 Use: "install",
32}
33
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020034// bootstrap is a flag controlling node parameters included in the installer
35// image. If set, the installed node will bootstrap a new cluster. Otherwise,
36// it will try to connect to the cluster which endpoints were provided with
37// the --endpoints flag.
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010038var bootstrap = installCmd.PersistentFlags().Bool("bootstrap", false, "Create a bootstrap installer image.")
Jan Schära9b060b2024-08-07 10:42:29 +020039var bootstrapTPMMode = flagdefs.TPMModePflag(installCmd.PersistentFlags(), "bootstrap-tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
40var bootstrapStorageSecurityPolicy = flagdefs.StorageSecurityPolicyPflag(installCmd.PersistentFlags(), "bootstrap-storage-security", cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_ENCRYPTION_AND_AUTHENTICATION, "Storage security policy to set on cluster")
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010041var bundlePath = installCmd.PersistentFlags().StringP("bundle", "b", "", "Path to the Metropolis bundle to be installed")
Timon Stampflib9701c32024-12-15 17:50:01 +010042var nodeParamPath = installCmd.PersistentFlags().String("node-params", "", "Path to the metropolis.proto.api.NodeParameters prototext file (advanced usage only)")
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020043
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020044func makeNodeParams() (*api.NodeParameters, error) {
Tim Windelschmidtb765f242024-05-08 01:40:02 +020045 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020046
Mateusz Zalega8234c162022-07-08 17:05:50 +020047 if err := os.MkdirAll(flags.configPath, 0700); err != nil && !os.IsExist(err) {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020048 return nil, fmt.Errorf("failed to create config directory: %w", err)
Lorenz Brune6573102021-11-02 14:15:37 +010049 }
Lorenz Brune6573102021-11-02 14:15:37 +010050
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020051 var params *api.NodeParameters
Timon Stampflib9701c32024-12-15 17:50:01 +010052 if *nodeParamPath != "" {
53 nodeParamsRaw, err := os.ReadFile(*nodeParamPath)
54 if err != nil {
55 return nil, fmt.Errorf("failed to read node-params file: %w", err)
56 }
57 if err := prototext.Unmarshal(nodeParamsRaw, params); err != nil {
58 return nil, fmt.Errorf("failed to parse node-params: %w", err)
59 }
60 } else {
61 params = &api.NodeParameters{}
62 }
63
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010064 if *bootstrap {
Jan Schär39f4f5c2024-10-29 09:41:50 +010065 if flags.cluster == "" {
66 return nil, fmt.Errorf("when bootstrapping a cluster, the --cluster parameter is required")
67 }
68 if err := common.ValidateClusterDomain(flags.cluster); err != nil {
69 return nil, fmt.Errorf("invalid cluster domain: %w", err)
70 }
71
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010072 // TODO(lorenz): Have a key management story for this
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010073 priv, err := core.GetOrMakeOwnerKey(flags.configPath)
74 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020075 return nil, fmt.Errorf("failed to generate or get owner key: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020076 }
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010077 pub := priv.Public().(ed25519.PublicKey)
Timon Stampflib9701c32024-12-15 17:50:01 +010078 params.Cluster = &api.NodeParameters_ClusterBootstrap_{
79 ClusterBootstrap: &api.NodeParameters_ClusterBootstrap{
80 OwnerPublicKey: pub,
81 InitialClusterConfiguration: &cpb.ClusterConfiguration{
82 ClusterDomain: flags.cluster,
83 StorageSecurityPolicy: *bootstrapStorageSecurityPolicy,
84 TpmMode: *bootstrapTPMMode,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020085 },
Lorenz Brune6573102021-11-02 14:15:37 +010086 },
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020087 }
88 } else {
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010089 cc, err := newAuthenticatedClient(ctx)
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020090 if err != nil {
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010091 return nil, fmt.Errorf("while creating client: %w", err)
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020092 }
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020093 mgmt := api.NewManagementClient(cc)
94 resT, err := mgmt.GetRegisterTicket(ctx, &api.GetRegisterTicketRequest{})
95 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020096 return nil, fmt.Errorf("while receiving register ticket: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020097 }
98 resI, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
99 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200100 return nil, fmt.Errorf("while receiving cluster directory: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +0200101 }
102
Timon Stampflib9701c32024-12-15 17:50:01 +0100103 params.Cluster = &api.NodeParameters_ClusterRegister_{
104 ClusterRegister: &api.NodeParameters_ClusterRegister{
105 RegisterTicket: resT.Ticket,
106 ClusterDirectory: resI.ClusterDirectory,
107 CaCertificate: resI.CaCertificate,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +0200108 },
109 }
Lorenz Brune6573102021-11-02 14:15:37 +0100110 }
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200111 return params, nil
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100112}
Lorenz Brune6573102021-11-02 14:15:37 +0100113
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200114func external(name, datafilePath string, flag *string) (fat32.SizedReader, error) {
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100115 if flag == nil || *flag == "" {
116 rPath, err := runfiles.Rlocation(datafilePath)
117 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200118 return nil, fmt.Errorf("no %s specified", name)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100119 }
120 df, err := os.ReadFile(rPath)
121 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200122 return nil, fmt.Errorf("can't read file: %w", err)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100123 }
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200124 return bytes.NewReader(df), nil
Lorenz Brune6573102021-11-02 14:15:37 +0100125 }
126
Tim Windelschmidt9ded9942024-04-08 21:30:17 +0200127 f, err := blkio.NewFileReader(*flag)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100128 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200129 return nil, fmt.Errorf("failed to open specified %s: %w", name, err)
Lorenz Brune6573102021-11-02 14:15:37 +0100130 }
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100131
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200132 return f, nil
Lorenz Brune6573102021-11-02 14:15:37 +0100133}
134
135func init() {
136 rootCmd.AddCommand(installCmd)
Lorenz Brune6573102021-11-02 14:15:37 +0100137}