blob: 1b9b8cbf2bf7db9b0c0d1398d2a183780bea3ba5 [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 (
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +02007 "context"
Lorenz Brune6573102021-11-02 14:15:37 +01008 "crypto/ed25519"
Lorenz Brun7a510192022-07-04 15:31:38 +00009 _ "embed"
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020010 "fmt"
Lorenz Brune6573102021-11-02 14:15:37 +010011 "os"
Tim Windelschmidtb765f242024-05-08 01:40:02 +020012 "os/signal"
Lorenz Brune6573102021-11-02 14:15:37 +010013
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010014 "github.com/bazelbuild/rules_go/go/runfiles"
Lorenz Brune6573102021-11-02 14:15:37 +010015 "github.com/spf13/cobra"
Timon Stampflib9701c32024-12-15 17:50:01 +010016 "google.golang.org/protobuf/encoding/prototext"
Lorenz Brune6573102021-11-02 14:15:37 +010017
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010018 "source.monogon.dev/metropolis/proto/api"
19 cpb "source.monogon.dev/metropolis/proto/common"
20
Jan Schära9b060b2024-08-07 10:42:29 +020021 "source.monogon.dev/metropolis/cli/flagdefs"
Lorenz Brune6573102021-11-02 14:15:37 +010022 "source.monogon.dev/metropolis/cli/metroctl/core"
Jan Schär39f4f5c2024-10-29 09:41:50 +010023 common "source.monogon.dev/metropolis/node"
Jan Schärc1b6df42025-03-20 08:52:18 +000024 "source.monogon.dev/osbase/structfs"
Lorenz Brune6573102021-11-02 14:15:37 +010025)
26
27var installCmd = &cobra.Command{
Lorenz Brun7a510192022-07-04 15:31:38 +000028 Short: "Contains subcommands to install Metropolis via different media.",
Lorenz Brune6573102021-11-02 14:15:37 +010029 Use: "install",
30}
31
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020032// bootstrap is a flag controlling node parameters included in the installer
33// image. If set, the installed node will bootstrap a new cluster. Otherwise,
34// it will try to connect to the cluster which endpoints were provided with
35// the --endpoints flag.
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010036var bootstrap = installCmd.PersistentFlags().Bool("bootstrap", false, "Create a bootstrap installer image.")
Jan Schära9b060b2024-08-07 10:42:29 +020037var bootstrapTPMMode = flagdefs.TPMModePflag(installCmd.PersistentFlags(), "bootstrap-tpm-mode", cpb.ClusterConfiguration_TPM_MODE_REQUIRED, "TPM mode to set on cluster")
38var 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 +010039var bundlePath = installCmd.PersistentFlags().StringP("bundle", "b", "", "Path to the Metropolis bundle to be installed")
Timon Stampflib9701c32024-12-15 17:50:01 +010040var 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 +020041
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020042func makeNodeParams() (*api.NodeParameters, error) {
Tim Windelschmidtb765f242024-05-08 01:40:02 +020043 ctx, _ := signal.NotifyContext(context.Background(), os.Interrupt)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020044
Mateusz Zalega8234c162022-07-08 17:05:50 +020045 if err := os.MkdirAll(flags.configPath, 0700); err != nil && !os.IsExist(err) {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020046 return nil, fmt.Errorf("failed to create config directory: %w", err)
Lorenz Brune6573102021-11-02 14:15:37 +010047 }
Lorenz Brune6573102021-11-02 14:15:37 +010048
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020049 var params *api.NodeParameters
Timon Stampflib9701c32024-12-15 17:50:01 +010050 if *nodeParamPath != "" {
51 nodeParamsRaw, err := os.ReadFile(*nodeParamPath)
52 if err != nil {
53 return nil, fmt.Errorf("failed to read node-params file: %w", err)
54 }
55 if err := prototext.Unmarshal(nodeParamsRaw, params); err != nil {
56 return nil, fmt.Errorf("failed to parse node-params: %w", err)
57 }
58 } else {
59 params = &api.NodeParameters{}
60 }
61
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010062 if *bootstrap {
Jan Schär39f4f5c2024-10-29 09:41:50 +010063 if flags.cluster == "" {
64 return nil, fmt.Errorf("when bootstrapping a cluster, the --cluster parameter is required")
65 }
66 if err := common.ValidateClusterDomain(flags.cluster); err != nil {
67 return nil, fmt.Errorf("invalid cluster domain: %w", err)
68 }
69
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010070 // TODO(lorenz): Have a key management story for this
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010071 priv, err := core.GetOrMakeOwnerKey(flags.configPath)
72 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020073 return nil, fmt.Errorf("failed to generate or get owner key: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020074 }
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010075 pub := priv.Public().(ed25519.PublicKey)
Timon Stampflib9701c32024-12-15 17:50:01 +010076 params.Cluster = &api.NodeParameters_ClusterBootstrap_{
77 ClusterBootstrap: &api.NodeParameters_ClusterBootstrap{
78 OwnerPublicKey: pub,
79 InitialClusterConfiguration: &cpb.ClusterConfiguration{
80 ClusterDomain: flags.cluster,
81 StorageSecurityPolicy: *bootstrapStorageSecurityPolicy,
82 TpmMode: *bootstrapTPMMode,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020083 },
Lorenz Brune6573102021-11-02 14:15:37 +010084 },
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020085 }
86 } else {
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010087 cc, err := newAuthenticatedClient(ctx)
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020088 if err != nil {
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +010089 return nil, fmt.Errorf("while creating client: %w", err)
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020090 }
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020091 mgmt := api.NewManagementClient(cc)
92 resT, err := mgmt.GetRegisterTicket(ctx, &api.GetRegisterTicketRequest{})
93 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020094 return nil, fmt.Errorf("while receiving register ticket: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020095 }
96 resI, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
97 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +020098 return nil, fmt.Errorf("while receiving cluster directory: %w", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020099 }
100
Timon Stampflib9701c32024-12-15 17:50:01 +0100101 params.Cluster = &api.NodeParameters_ClusterRegister_{
102 ClusterRegister: &api.NodeParameters_ClusterRegister{
103 RegisterTicket: resT.Ticket,
104 ClusterDirectory: resI.ClusterDirectory,
105 CaCertificate: resI.CaCertificate,
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +0200106 },
107 }
Lorenz Brune6573102021-11-02 14:15:37 +0100108 }
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200109 return params, nil
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100110}
Lorenz Brune6573102021-11-02 14:15:37 +0100111
Jan Schärc1b6df42025-03-20 08:52:18 +0000112func external(name, datafilePath string, flag *string) (structfs.Blob, error) {
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100113 if flag == nil || *flag == "" {
114 rPath, err := runfiles.Rlocation(datafilePath)
115 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200116 return nil, fmt.Errorf("no %s specified", name)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100117 }
Jan Schärc1b6df42025-03-20 08:52:18 +0000118 return structfs.OSPathBlob(rPath)
Lorenz Brune6573102021-11-02 14:15:37 +0100119 }
Jan Schärc1b6df42025-03-20 08:52:18 +0000120 f, err := structfs.OSPathBlob(*flag)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100121 if err != nil {
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200122 return nil, fmt.Errorf("failed to open specified %s: %w", name, err)
Lorenz Brune6573102021-11-02 14:15:37 +0100123 }
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100124
Tim Windelschmidt0b4fb8c2024-09-18 17:34:23 +0200125 return f, nil
Lorenz Brune6573102021-11-02 14:15:37 +0100126}
127
128func init() {
129 rootCmd.AddCommand(installCmd)
Lorenz Brune6573102021-11-02 14:15:37 +0100130}