blob: fdc51b005674a84dccc91f975ed499d3ea048a8b [file] [log] [blame]
Lorenz Brune6573102021-11-02 14:15:37 +01001package main
2
3import (
Serge Bazanski97783222021-12-14 16:04:26 +01004 "bytes"
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +02005 "context"
Lorenz Brune6573102021-11-02 14:15:37 +01006 "crypto/ed25519"
Lorenz Brun7a510192022-07-04 15:31:38 +00007 _ "embed"
Lorenz Brune6573102021-11-02 14:15:37 +01008 "log"
9 "os"
Serge Bazanskibdc803b2023-03-27 10:55:09 +020010 "strings"
Lorenz Brune6573102021-11-02 14:15:37 +010011
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010012 "github.com/bazelbuild/rules_go/go/runfiles"
Lorenz Brune6573102021-11-02 14:15:37 +010013 "github.com/spf13/cobra"
14
Tim Windelschmidt2a1d1b22024-02-06 07:07:42 +010015 "source.monogon.dev/metropolis/proto/api"
16 cpb "source.monogon.dev/metropolis/proto/common"
17
Lorenz Brune6573102021-11-02 14:15:37 +010018 "source.monogon.dev/metropolis/cli/metroctl/core"
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020019 clicontext "source.monogon.dev/metropolis/cli/pkg/context"
Lorenz Brunad131882023-06-28 16:42:20 +020020 "source.monogon.dev/metropolis/pkg/blkio"
21 "source.monogon.dev/metropolis/pkg/fat32"
Lorenz Brune6573102021-11-02 14:15:37 +010022)
23
24var installCmd = &cobra.Command{
Lorenz Brun7a510192022-07-04 15:31:38 +000025 Short: "Contains subcommands to install Metropolis via different media.",
Lorenz Brune6573102021-11-02 14:15:37 +010026 Use: "install",
27}
28
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020029// bootstrap is a flag controlling node parameters included in the installer
30// image. If set, the installed node will bootstrap a new cluster. Otherwise,
31// it will try to connect to the cluster which endpoints were provided with
32// the --endpoints flag.
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010033var bootstrap = installCmd.PersistentFlags().Bool("bootstrap", false, "Create a bootstrap installer image.")
34var bootstrapTPMMode = installCmd.PersistentFlags().String("bootstrap-tpm-mode", "required", "TPM mode to set on cluster (required, best-effort, disabled)")
35var bootstrapStorageSecurityPolicy = installCmd.PersistentFlags().String("bootstrap-storage-security", "needs-encryption-and-authentication", "Storage security policy to set on cluster (permissive, needs-encryption, needs-encryption-and-authentication, needs-insecure)")
36var bundlePath = installCmd.PersistentFlags().StringP("bundle", "b", "", "Path to the Metropolis bundle to be installed")
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020037
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010038func makeNodeParams() *api.NodeParameters {
Serge Bazanskibdc803b2023-03-27 10:55:09 +020039 var tpmMode cpb.ClusterConfiguration_TPMMode
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010040 switch strings.ToLower(*bootstrapTPMMode) {
Serge Bazanskibdc803b2023-03-27 10:55:09 +020041 case "required", "require":
42 tpmMode = cpb.ClusterConfiguration_TPM_MODE_REQUIRED
43 case "best-effort", "besteffort":
44 tpmMode = cpb.ClusterConfiguration_TPM_MODE_BEST_EFFORT
45 case "disabled", "disable":
46 tpmMode = cpb.ClusterConfiguration_TPM_MODE_DISABLED
47 default:
48 log.Fatalf("Invalid --bootstrap-tpm-mode (must be one of: required, best-effort, disabled)")
49 }
50
Tim Windelschmidtc09327c2023-06-14 17:48:56 +020051 var bootstrapStorageSecurity cpb.ClusterConfiguration_StorageSecurityPolicy
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010052 switch strings.ToLower(*bootstrapStorageSecurityPolicy) {
Tim Windelschmidtc09327c2023-06-14 17:48:56 +020053 case "permissive":
54 bootstrapStorageSecurity = cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_PERMISSIVE
55 case "needs-encryption":
56 bootstrapStorageSecurity = cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_ENCRYPTION
57 case "needs-encryption-and-authentication":
58 bootstrapStorageSecurity = cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_ENCRYPTION_AND_AUTHENTICATION
59 case "needs-insecure":
60 bootstrapStorageSecurity = cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_INSECURE
61 default:
62
63 log.Fatalf("Invalid --bootstrap-storage-security (must be one of: permissive, needs-encryption, needs-encryption-and-authentication, needs-insecure)")
64 }
65
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020066 ctx := clicontext.WithInterrupt(context.Background())
67
Mateusz Zalega8234c162022-07-08 17:05:50 +020068 if err := os.MkdirAll(flags.configPath, 0700); err != nil && !os.IsExist(err) {
Lorenz Brune6573102021-11-02 14:15:37 +010069 log.Fatalf("Failed to create config directory: %v", err)
70 }
Lorenz Brune6573102021-11-02 14:15:37 +010071
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020072 var params *api.NodeParameters
Tim Windelschmidt7006caf2024-02-27 16:49:39 +010073 if *bootstrap {
74 // TODO(lorenz): Have a key management story for this
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010075 priv, err := core.GetOrMakeOwnerKey(flags.configPath)
76 if err != nil {
77 log.Fatalf("Failed to generate or get owner key: %v", err)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020078 }
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010079 pub := priv.Public().(ed25519.PublicKey)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020080 params = &api.NodeParameters{
81 Cluster: &api.NodeParameters_ClusterBootstrap_{
82 ClusterBootstrap: &api.NodeParameters_ClusterBootstrap{
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010083 OwnerPublicKey: pub,
Serge Bazanskibdc803b2023-03-27 10:55:09 +020084 InitialClusterConfiguration: &cpb.ClusterConfiguration{
Tim Windelschmidtc09327c2023-06-14 17:48:56 +020085 StorageSecurityPolicy: bootstrapStorageSecurity,
86 TpmMode: tpmMode,
Serge Bazanskibdc803b2023-03-27 10:55:09 +020087 },
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020088 },
Lorenz Brune6573102021-11-02 14:15:37 +010089 },
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020090 }
91 } else {
Mateusz Zalegadb75e212022-08-04 17:31:34 +020092 cc := dialAuthenticated(ctx)
Mateusz Zalegad5f2f7a2022-07-05 18:48:56 +020093 mgmt := api.NewManagementClient(cc)
94 resT, err := mgmt.GetRegisterTicket(ctx, &api.GetRegisterTicketRequest{})
95 if err != nil {
96 log.Fatalf("While receiving register ticket: %v", err)
97 }
98 resI, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
99 if err != nil {
100 log.Fatalf("While receiving cluster directory: %v", err)
101 }
102
103 params = &api.NodeParameters{
104 Cluster: &api.NodeParameters_ClusterRegister_{
105 ClusterRegister: &api.NodeParameters_ClusterRegister{
106 RegisterTicket: resT.Ticket,
107 ClusterDirectory: resI.ClusterDirectory,
108 CaCertificate: resI.CaCertificate,
109 },
110 },
111 }
Lorenz Brune6573102021-11-02 14:15:37 +0100112 }
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100113 return params
114}
Lorenz Brune6573102021-11-02 14:15:37 +0100115
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100116func external(name, datafilePath string, flag *string) fat32.SizedReader {
117 if flag == nil || *flag == "" {
118 rPath, err := runfiles.Rlocation(datafilePath)
119 if err != nil {
120 log.Fatalf("No %s specified", name)
121 }
122 df, err := os.ReadFile(rPath)
123 if err != nil {
124 log.Fatalf("Cant read file: %v", err)
125 }
126 return bytes.NewReader(df)
Lorenz Brune6573102021-11-02 14:15:37 +0100127 }
128
Tim Windelschmidt9ded9942024-04-08 21:30:17 +0200129 f, err := blkio.NewFileReader(*flag)
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100130 if err != nil {
131 log.Fatalf("Failed to open specified %s: %v", name, err)
Lorenz Brune6573102021-11-02 14:15:37 +0100132 }
Tim Windelschmidt7006caf2024-02-27 16:49:39 +0100133
134 return f
Lorenz Brune6573102021-11-02 14:15:37 +0100135}
136
137func init() {
138 rootCmd.AddCommand(installCmd)
Lorenz Brune6573102021-11-02 14:15:37 +0100139}