blob: ace12f718ebcd9cd45ec6b03417236b9489ae624 [file] [log] [blame]
Lorenz Brunae0d90d2019-09-05 17:53:56 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package main
18
19import (
20 "io/ioutil"
21 "os"
22 "os/exec"
23 "os/signal"
Lorenz Brunf95909d2019-09-11 19:48:26 +020024 "runtime/debug"
25 "smalltown/internal/network"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020026 node2 "smalltown/internal/node"
27 "smalltown/internal/storage"
28 "smalltown/pkg/tpm"
Lorenz Brunae0d90d2019-09-05 17:53:56 +020029
30 "go.uber.org/zap"
31 "golang.org/x/sys/unix"
32)
33
34func main() {
Lorenz Brunf95909d2019-09-11 19:48:26 +020035 defer func() {
36 if r := recover(); r != nil {
37 fmt.Println("Init panicked:", r)
38 debug.PrintStack()
39 }
40 unix.Sync()
41 unix.Reboot(unix.LINUX_REBOOT_CMD_POWER_OFF)
42 }()
Lorenz Brunae0d90d2019-09-05 17:53:56 +020043 logger, err := zap.NewDevelopment()
44 if err != nil {
45 panic(err)
46 }
47 logger.Info("Starting Smalltown Init")
48
49 // Set up bare minimum mounts
50 if err := os.Mkdir("/sys", 0755); err != nil {
51 panic(err)
52 }
53 if err := unix.Mount("sysfs", "/sys", "sysfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, ""); err != nil {
54 panic(err)
55 }
56
57 if err := os.Mkdir("/proc", 0755); err != nil {
58 panic(err)
59 }
60 if err := unix.Mount("procfs", "/proc", "proc", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, ""); err != nil {
61 panic(err)
62 }
63
64 signalChannel := make(chan os.Signal, 2)
65 signal.Notify(signalChannel)
66
67 if err := storage.FindPartitions(); err != nil {
68 logger.Panic("Failed to search for partitions", zap.Error(err))
69 }
70
71 if err := os.Mkdir("/esp", 0755); err != nil {
72 panic(err)
73 }
74
75 if err := unix.Mount(storage.ESPDevicePath, "/esp", "vfat", unix.MS_NOEXEC|unix.MS_NODEV|unix.MS_SYNC, ""); err != nil {
76 logger.Panic("Failed to mount ESP partition", zap.Error(err))
77 }
78
79 if err := tpm.Initialize(logger.With(zap.String("component", "tpm"))); err != nil {
80 logger.Panic("Failed to initialize TPM 2.0", zap.Error(err))
81 }
82
83 // TODO(lorenz): This really doesn't belong here and needs to be asynchronous as well
84 var keyLocation = "/esp/EFI/smalltown/data-key.bin"
85 sealedKeyFile, err := os.Open(keyLocation)
86 if os.IsNotExist(err) {
87 logger.Info("Initializing encrypted storage, this might take a while...")
88 key, err := tpm.GenerateSafeKey(256 / 8)
89 if err != nil {
90 panic(err)
91 }
92 sealedKey, err := tpm.Seal(key, tpm.SecureBootPCRs)
93 if err != nil {
94 panic(err)
95 }
96 if err := storage.InitializeEncryptedBlockDevice("data", storage.SmalltownDataCryptPath, key); err != nil {
97 panic(err)
98 }
99 mkfsCmd := exec.Command("/bin/mkfs.xfs", "-qf", "/dev/data")
100 if _, err := mkfsCmd.Output(); err != nil {
101 panic(err)
102 }
103 // Existence of this file indicates that the encrypted storage has been successfully initialized
104 if err := ioutil.WriteFile(keyLocation, sealedKey, 0600); err != nil {
105 panic(err)
106 }
107 logger.Info("Initialized encrypted storage")
108 } else if err != nil {
109 panic(err)
110 } else {
111 sealedKey, err := ioutil.ReadAll(sealedKeyFile)
112 if err != nil {
113 panic(err)
114 }
115 key, err := tpm.Unseal(sealedKey)
116 if err != nil {
117 panic(err)
118 }
119 if err := storage.MapEncryptedBlockDevice("data", storage.SmalltownDataCryptPath, key); err != nil {
120 panic(err)
121 }
122 logger.Info("Opened encrypted storage")
123 }
124 sealedKeyFile.Close()
125
126 if err := os.Mkdir("/data", 0755); err != nil {
127 panic(err)
128 }
129
130 if err := unix.Mount("/dev/data", "/data", "xfs", unix.MS_NOEXEC|unix.MS_NODEV, ""); err != nil {
131 panic(err)
132 }
133
Lorenz Brunf95909d2019-09-11 19:48:26 +0200134 networkSvc, err := network.NewNetworkService(network.Config{}, logger.With(zap.String("component", "network")))
135 if err != nil {
136 panic(err)
137 }
138 networkSvc.Start()
139
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200140 node, err := node2.NewSmalltownNode(logger, "/esp/EFI/smalltown", "/data", 7833, 7834)
141 if err != nil {
142 panic(err)
143 }
144
145 err = node.Start()
146 if err != nil {
147 panic(err)
148 }
149
150 // We're PID1, so orphaned processes get reparented to us to clean up
151 for {
152 sig := <-signalChannel
153 switch sig {
154 case unix.SIGCHLD:
155 var status unix.WaitStatus
156 var rusage unix.Rusage
157 for {
Lorenz Brunf95909d2019-09-11 19:48:26 +0200158 res, err := unix.Wait4(-1, &status, unix.WNOHANG, &rusage)
159 if err != nil && err != unix.ECHILD {
Lorenz Brunae0d90d2019-09-05 17:53:56 +0200160 logger.Error("Failed to wait on orphaned child", zap.Error(err))
161 break
162 }
163 if res <= 0 {
164 break
165 }
166 }
167 // TODO(lorenz): We can probably get more than just SIGCHLD as init, but I can't think
168 // of any others right now, just log them in case we hit any of them.
169 default:
170 logger.Warn("Got unexpected signal", zap.String("signal", sig.String()))
171 }
172 }
173}