Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a3e7ecc

Browse files
cmaglieumbynos
andauthoredFeb 10, 2023
Cleanup directories handling (#765)
* Sligltly simplified getDefaultConfigDir * Some code make-up * Added method to directly get data-dir * Added method to directly get logs dir * Removed now useless local variable * certs are now created in `.arduino-create` folder * Better use of go-paths library * Perform certificates migration if needed --------- Co-authored-by: Umberto Baldi <[email protected]>
1 parent 8b9ab21 commit a3e7ecc

File tree

4 files changed

+142
-88
lines changed

4 files changed

+142
-88
lines changed
 

‎certificates.go

Lines changed: 87 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import (
1616
"crypto/x509/pkix"
1717
"encoding/pem"
1818
"fmt"
19-
"io/ioutil"
2019
"math/big"
2120
"net"
2221
"os"
2322
"strings"
2423
"text/template"
2524
"time"
2625

26+
"github.com/arduino/go-paths-helper"
2727
"github.com/gin-gonic/gin"
2828
log "github.com/sirupsen/logrus"
2929
)
@@ -133,12 +133,38 @@ func generateSingleCertificate(isCa bool) (*x509.Certificate, error) {
133133
return &template, nil
134134
}
135135

136-
func generateCertificates() {
136+
// migrateCertificatesGeneratedWithOldAgentVersions checks if certificates generated
137+
// with an old version of the Agent needs to be migrated to the current certificates
138+
// directory, and performs the migration if needed.
139+
func migrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) {
140+
if certsDir.Join("ca.cert.pem").Exist() {
141+
// The new certificates are already set-up, nothing to do
142+
return
143+
}
144+
145+
fileList := []string{
146+
"ca.key.pem",
147+
"ca.cert.pem",
148+
"ca.cert.cer",
149+
"key.pem",
150+
"cert.pem",
151+
"cert.cer",
152+
}
153+
oldCertsDirPath, _ := os.Executable()
154+
oldCertsDir := paths.New(oldCertsDirPath)
155+
for _, fileName := range fileList {
156+
oldCert := oldCertsDir.Join(fileName)
157+
if oldCert.Exist() {
158+
oldCert.CopyTo(certsDir.Join(fileName))
159+
}
160+
}
161+
}
137162

138-
os.Remove("ca.cert.pem")
139-
os.Remove("ca.key.pem")
140-
os.Remove("cert.pem")
141-
os.Remove("key.pem")
163+
func generateCertificates(certsDir *paths.Path) {
164+
certsDir.Join("ca.cert.pem").Remove()
165+
certsDir.Join("ca.key.pem").Remove()
166+
certsDir.Join("cert.pem").Remove()
167+
certsDir.Join("key.pem").Remove()
142168

143169
// Create the key for the certification authority
144170
caKey, err := generateKey("P256")
@@ -147,36 +173,44 @@ func generateCertificates() {
147173
os.Exit(1)
148174
}
149175

150-
keyOut, err := os.OpenFile("ca.key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
151-
if err != nil {
152-
log.Error(err.Error())
153-
os.Exit(1)
176+
{
177+
keyOutPath := certsDir.Join("ca.key.pem").String()
178+
keyOut, err := os.OpenFile(keyOutPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) // Save key with user-only permission 0600
179+
if err != nil {
180+
log.Error(err.Error())
181+
os.Exit(1)
182+
}
183+
pem.Encode(keyOut, pemBlockForKey(caKey))
184+
keyOut.Close()
185+
log.Printf("written %s", keyOutPath)
154186
}
155-
pem.Encode(keyOut, pemBlockForKey(caKey))
156-
keyOut.Close()
157-
log.Println("written ca.key.pem")
158187

159188
// Create the certification authority
160189
caTemplate, err := generateSingleCertificate(true)
161-
162190
if err != nil {
163191
log.Error(err.Error())
164192
os.Exit(1)
165193
}
166194

167195
derBytes, _ := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, publicKey(caKey), caKey)
168196

169-
certOut, err := os.Create("ca.cert.pem")
170-
if err != nil {
171-
log.Error(err.Error())
172-
os.Exit(1)
197+
{
198+
caCertOutPath := certsDir.Join("ca.cert.pem")
199+
caCertOut, err := caCertOutPath.Create()
200+
if err != nil {
201+
log.Error(err.Error())
202+
os.Exit(1)
203+
}
204+
pem.Encode(caCertOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
205+
caCertOut.Close()
206+
log.Printf("written %s", caCertOutPath)
173207
}
174-
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
175-
certOut.Close()
176-
log.Print("written ca.cert.pem")
177208

178-
ioutil.WriteFile("ca.cert.cer", derBytes, 0644)
179-
log.Print("written ca.cert.cer")
209+
{
210+
caCertPath := certsDir.Join("ca.cert.cer")
211+
caCertPath.WriteFile(derBytes)
212+
log.Printf("written %s", caCertPath)
213+
}
180214

181215
// Create the key for the final certificate
182216
key, err := generateKey("P256")
@@ -185,37 +219,44 @@ func generateCertificates() {
185219
os.Exit(1)
186220
}
187221

188-
keyOut, err = os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
189-
if err != nil {
190-
log.Error(err.Error())
191-
os.Exit(1)
222+
{
223+
keyOutPath := certsDir.Join("key.pem").String()
224+
keyOut, err := os.OpenFile(keyOutPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) // Save key with user-only permission 0600
225+
if err != nil {
226+
log.Error(err.Error())
227+
os.Exit(1)
228+
}
229+
pem.Encode(keyOut, pemBlockForKey(key))
230+
keyOut.Close()
231+
log.Printf("written %s", keyOutPath)
192232
}
193-
pem.Encode(keyOut, pemBlockForKey(key))
194-
keyOut.Close()
195-
log.Println("written key.pem")
196233

197234
// Create the final certificate
198235
template, err := generateSingleCertificate(false)
199-
200236
if err != nil {
201237
log.Error(err.Error())
202238
os.Exit(1)
203239
}
204240

205241
derBytes, _ = x509.CreateCertificate(rand.Reader, template, caTemplate, publicKey(key), caKey)
206242

207-
certOut, err = os.Create("cert.pem")
208-
if err != nil {
209-
log.Error(err.Error())
210-
os.Exit(1)
243+
{
244+
certOutPath := certsDir.Join("cert.pem").String()
245+
certOut, err := os.Create(certOutPath)
246+
if err != nil {
247+
log.Error(err.Error())
248+
os.Exit(1)
249+
}
250+
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
251+
certOut.Close()
252+
log.Printf("written %s", certOutPath)
211253
}
212-
pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
213-
certOut.Close()
214-
log.Print("written cert.pem")
215-
216-
ioutil.WriteFile("cert.cer", derBytes, 0644)
217-
log.Print("written cert.cer")
218254

255+
{
256+
certPath := certsDir.Join("cert.cer")
257+
certPath.WriteFile(derBytes)
258+
log.Printf("written %s", certPath)
259+
}
219260
}
220261

221262
func certHandler(c *gin.Context) {
@@ -230,14 +271,14 @@ func certHandler(c *gin.Context) {
230271
}
231272

232273
func deleteCertHandler(c *gin.Context) {
233-
DeleteCertificates()
274+
DeleteCertificates(getCertificatesDir())
234275
}
235276

236277
// DeleteCertificates will delete the certificates
237-
func DeleteCertificates() {
238-
os.Remove("ca.cert.pem")
239-
os.Remove("ca.cert.cer")
240-
os.Remove("ca.key.pem")
278+
func DeleteCertificates(certDir *paths.Path) {
279+
certDir.Join("ca.cert.pem").Remove()
280+
certDir.Join("ca.cert.cer").Remove()
281+
certDir.Join("ca.key.pem").Remove()
241282
}
242283

243284
const noFirefoxTemplateHTML = `<!DOCTYPE html>

‎config.go

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,41 @@ package main
1717

1818
import (
1919
_ "embed"
20-
"fmt"
2120
"os"
2221

2322
"github.com/arduino/go-paths-helper"
2423
log "github.com/sirupsen/logrus"
2524
)
2625

27-
// getDefaultArduinoCreateConfigDir returns the full path to the default arduino create agent data directory
28-
func getDefaultArduinoCreateConfigDir() (*paths.Path, error) {
26+
// getCertificatesDir return the directory where SSL certificates are saved
27+
func getCertificatesDir() *paths.Path {
28+
return getDataDir()
29+
}
30+
31+
// getDataDir returns the full path to the default Arduino Create Agent data directory.
32+
func getDataDir() *paths.Path {
33+
userDir, err := os.UserHomeDir()
34+
if err != nil {
35+
log.Panicf("Could not get user dir: %s", err)
36+
}
37+
dataDir := paths.New(userDir, ".arduino-create")
38+
if err := dataDir.MkdirAll(); err != nil {
39+
log.Panicf("Could not create data dir: %s", err)
40+
}
41+
return dataDir
42+
}
43+
44+
// getLogsDir return the directory where logs are saved
45+
func getLogsDir() *paths.Path {
46+
logsDir := getDataDir().Join("logs")
47+
if err := logsDir.MkdirAll(); err != nil {
48+
log.Panicf("Can't create logs dir: %s", err)
49+
}
50+
return logsDir
51+
}
52+
53+
// getDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory.
54+
func getDefaultConfigDir() *paths.Path {
2955
// UserConfigDir returns the default root directory to use
3056
// for user-specific configuration data. Users should create
3157
// their own application-specific subdirectory within this
@@ -43,14 +69,14 @@ func getDefaultArduinoCreateConfigDir() (*paths.Path, error) {
4369
// is not defined), then it will return an error.
4470
configDir, err := os.UserConfigDir()
4571
if err != nil {
46-
return nil, err
72+
log.Panicf("Can't get user home dir: %s", err)
4773
}
4874

4975
agentConfigDir := paths.New(configDir, "ArduinoCreateAgent")
5076
if err := agentConfigDir.MkdirAll(); err != nil {
51-
return nil, fmt.Errorf("cannot create config dir: %s", err)
77+
log.Panicf("Can't create config dir: %s", err)
5278
}
53-
return agentConfigDir, nil
79+
return agentConfigDir
5480
}
5581

5682
//go:embed config.ini

‎hub.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ func checkCmd(m []byte) {
182182
} else if strings.HasPrefix(sl, "downloadtool") {
183183
// Always delete root certificates when we receive a downloadtool command
184184
// Useful if the install procedure was not followed strictly (eg. manually)
185-
DeleteCertificates()
185+
DeleteCertificates(getCertificatesDir())
186186
go func() {
187187
args := strings.Split(s, " ")
188188
var tool, toolVersion, pack, behaviour string

‎main.go

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,17 @@ func main() {
128128

129129
// Generate certificates
130130
if *genCert {
131-
generateCertificates()
131+
generateCertificates(getCertificatesDir())
132132
os.Exit(0)
133133
}
134+
// Check if certificates made with Agent <=1.2.7 needs to be moved over the new location
135+
migrateCertificatesGeneratedWithOldAgentVersions(getCertificatesDir())
134136

135137
// Launch main loop in a goroutine
136138
go loop()
137139

138140
// SetupSystray is the main thread
139-
configDir, err := getDefaultArduinoCreateConfigDir()
140-
if err != nil {
141-
log.Panicf("Can't open defaul configuration dir: %s", err)
142-
}
141+
configDir := getDefaultConfigDir()
143142
Systray = systray.Systray{
144143
Hibernate: *hibernate,
145144
Version: version + "-" + commit,
@@ -150,25 +149,19 @@ func main() {
150149
ConfigDir: configDir,
151150
}
152151

153-
path, err := os.Executable()
154-
if err != nil {
155-
panic(err)
156-
}
157-
158152
// If the executable is temporary, copy it to the full path, then restart
159-
if strings.Contains(path, "-temp") {
160-
newPath := updater.BinPath(path)
161-
err := copyExe(path, newPath)
162-
if err != nil {
153+
if src, err := os.Executable(); err != nil {
154+
panic(err)
155+
} else if strings.Contains(src, "-temp") {
156+
newPath := updater.BinPath(src)
157+
if err := copyExe(src, newPath); err != nil {
163158
log.Println("Copy error: ", err)
164159
panic(err)
165160
}
166-
167161
Systray.Update(newPath)
168162
} else {
169163
// Otherwise copy to a path with -temp suffix
170-
err := copyExe(path, updater.TempPath(path))
171-
if err != nil {
164+
if err := copyExe(src, updater.TempPath(src)); err != nil {
172165
panic(err)
173166
}
174167
Systray.Start()
@@ -197,15 +190,9 @@ func loop() {
197190
log.SetLevel(log.InfoLevel)
198191
log.SetOutput(os.Stdout)
199192

200-
// the important folders of the agent
201-
src, _ := os.Executable()
202-
srcPath := paths.New(src) // The path of the agent's binary
203-
srcDir := srcPath.Parent() // The directory of the agent's binary
204-
agentDir, err := getDefaultArduinoCreateConfigDir()
205-
206193
// Instantiate Tools
207194
Tools = tools.Tools{
208-
Directory: agentDir.String(),
195+
Directory: getDataDir().String(),
209196
IndexURL: *indexURL,
210197
Logger: func(msg string) {
211198
mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg}
@@ -216,6 +203,7 @@ func loop() {
216203
Tools.Init(requiredToolsAPILevel)
217204

218205
// Let's handle the config
206+
configDir := getDefaultConfigDir()
219207
var configPath *paths.Path
220208

221209
// see if the env var is defined, if it is take the config from there, this will override the default path
@@ -225,13 +213,14 @@ func loop() {
225213
log.Panicf("config from env var %s does not exists", envConfig)
226214
}
227215
log.Infof("using config from env variable: %s", configPath)
228-
} else if defaultConfigPath := agentDir.Join("config.ini"); defaultConfigPath.Exist() {
216+
} else if defaultConfigPath := configDir.Join("config.ini"); defaultConfigPath.Exist() {
229217
// by default take the config from the ~/.arduino-create/config.ini file
230218
configPath = defaultConfigPath
231219
log.Infof("using config from default: %s", configPath)
232220
} else {
233-
// take the config from the old folder where the agent's binary sits
234-
oldConfigPath := srcDir.Join("config.ini")
221+
// Fall back to the old config.ini location
222+
src, _ := os.Executable()
223+
oldConfigPath := paths.New(src).Parent().Join("config.ini")
235224
if oldConfigPath.Exist() {
236225
err := oldConfigPath.CopyTo(defaultConfigPath)
237226
if err != nil {
@@ -243,7 +232,7 @@ func loop() {
243232
}
244233
}
245234
if configPath == nil {
246-
configPath = generateConfig(agentDir)
235+
configPath = generateConfig(configDir)
247236
}
248237

249238
// Parse the config.ini
@@ -352,10 +341,7 @@ func loop() {
352341
if *crashreport {
353342
logFilename := "crashreport_" + time.Now().Format("20060102150405") + ".log"
354343
// handle logs directory creation
355-
logsDir := agentDir.Join("logs")
356-
if logsDir.NotExist() {
357-
logsDir.Mkdir()
358-
}
344+
logsDir := getLogsDir()
359345
logFile, err := os.OpenFile(logsDir.Join(logFilename).String(), os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644)
360346
if err != nil {
361347
log.Print("Cannot create file used for crash-report")
@@ -415,12 +401,13 @@ func loop() {
415401
r.POST("/update", updateHandler)
416402

417403
// Mount goa handlers
418-
goa := v2.Server(agentDir.String())
404+
goa := v2.Server(getDataDir().String())
419405
r.Any("/v2/*path", gin.WrapH(goa))
420406

421407
go func() {
422408
// check if certificates exist; if not, use plain http
423-
if srcDir.Join("cert.pem").NotExist() {
409+
certsDir := getCertificatesDir()
410+
if certsDir.Join("cert.pem").NotExist() {
424411
log.Error("Could not find HTTPS certificate. Using plain HTTP only.")
425412
return
426413
}
@@ -431,7 +418,7 @@ func loop() {
431418
for i < end {
432419
i = i + 1
433420
portSSL = ":" + strconv.Itoa(i)
434-
if err := r.RunTLS(*address+portSSL, srcDir.Join("cert.pem").String(), srcDir.Join("key.pem").String()); err != nil {
421+
if err := r.RunTLS(*address+portSSL, certsDir.Join("cert.pem").String(), certsDir.Join("key.pem").String()); err != nil {
435422
log.Printf("Error trying to bind to port: %v, so exiting...", err)
436423
continue
437424
} else {

0 commit comments

Comments
 (0)
Please sign in to comment.