From c6113951fa3c1a4227556222f1d7d16a7f0a960f Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 23 Mar 2023 14:32:35 +0100 Subject: [PATCH 01/22] move `config.go` to it's own package and make functions public. This way we have a single source of truth. --- .gitignore | 2 ++ certificates.go | 3 ++- config.go => config/config.go | 29 ++++++++++++++++------------- hub.go | 3 ++- main.go | 19 ++++++++++--------- systray/systray_real.go | 15 +++------------ 6 files changed, 35 insertions(+), 36 deletions(-) rename config.go => config/config.go (79%) diff --git a/.gitignore b/.gitignore index 97789fc52..1f0bc1f3c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,8 @@ public/ artifacts* logs/ +config/config.ini + # IDEs config .idea .vscode diff --git a/certificates.go b/certificates.go index af8f6c3a2..9bb94d59a 100644 --- a/certificates.go +++ b/certificates.go @@ -23,6 +23,7 @@ import ( "text/template" "time" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/go-paths-helper" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -271,7 +272,7 @@ func certHandler(c *gin.Context) { } func deleteCertHandler(c *gin.Context) { - DeleteCertificates(getCertificatesDir()) + DeleteCertificates(config.GetCertificatesDir()) } // DeleteCertificates will delete the certificates diff --git a/config.go b/config/config.go similarity index 79% rename from config.go rename to config/config.go index 182e24c52..0a1722953 100644 --- a/config.go +++ b/config/config.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -package main +package config import ( _ "embed" @@ -23,13 +23,13 @@ import ( log "github.com/sirupsen/logrus" ) -// getCertificatesDir return the directory where SSL certificates are saved -func getCertificatesDir() *paths.Path { - return getDataDir() +// GetCertificatesDir return the directory where SSL certificates are saved +func GetCertificatesDir() *paths.Path { + return GetDataDir() } -// getDataDir returns the full path to the default Arduino Create Agent data directory. -func getDataDir() *paths.Path { +// GetDataDir returns the full path to the default Arduino Create Agent data directory. +func GetDataDir() *paths.Path { userDir, err := os.UserHomeDir() if err != nil { log.Panicf("Could not get user dir: %s", err) @@ -41,17 +41,17 @@ func getDataDir() *paths.Path { return dataDir } -// getLogsDir return the directory where logs are saved -func getLogsDir() *paths.Path { - logsDir := getDataDir().Join("logs") +// GetLogsDir return the directory where logs are saved +func GetLogsDir() *paths.Path { + logsDir := GetDataDir().Join("logs") if err := logsDir.MkdirAll(); err != nil { log.Panicf("Can't create logs dir: %s", err) } return logsDir } -// getDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory. -func getDefaultConfigDir() *paths.Path { +// GetDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory. +func GetDefaultConfigDir() *paths.Path { // UserConfigDir returns the default root directory to use // for user-specific configuration data. Users should create // their own application-specific subdirectory within this @@ -79,13 +79,16 @@ func getDefaultConfigDir() *paths.Path { return agentConfigDir } +// https://github.com/golang/go/issues/46056 +// +//go:generate cp -r ../config.ini config.ini //go:embed config.ini var configContent []byte -// generateConfig function will take a directory path as an input +// GenerateConfig function will take a directory path as an input // and will write the default config,ini file to that directory, // it will panic if something goes wrong -func generateConfig(destDir *paths.Path) *paths.Path { +func GenerateConfig(destDir *paths.Path) *paths.Path { configPath := destDir.Join("config.ini") // generate the config.ini file directly in destDir diff --git a/hub.go b/hub.go index f59c6460e..eae24328c 100755 --- a/hub.go +++ b/hub.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/arduino-create-agent/upload" log "github.com/sirupsen/logrus" ) @@ -182,7 +183,7 @@ func checkCmd(m []byte) { } else if strings.HasPrefix(sl, "downloadtool") { // Always delete root certificates when we receive a downloadtool command // Useful if the install procedure was not followed strictly (eg. manually) - DeleteCertificates(getCertificatesDir()) + DeleteCertificates(config.GetCertificatesDir()) go func() { args := strings.Split(s, " ") var tool, toolVersion, pack, behaviour string diff --git a/main.go b/main.go index f6bc3afd6..f2c4cd8fb 100755 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( "time" cors "github.com/andela/gin-cors" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/arduino-create-agent/systray" "github.com/arduino/arduino-create-agent/tools" "github.com/arduino/arduino-create-agent/updater" @@ -127,17 +128,17 @@ func main() { // Generate certificates if *genCert { - generateCertificates(getCertificatesDir()) + generateCertificates(config.GetCertificatesDir()) os.Exit(0) } // Check if certificates made with Agent <=1.2.7 needs to be moved over the new location - migrateCertificatesGeneratedWithOldAgentVersions(getCertificatesDir()) + migrateCertificatesGeneratedWithOldAgentVersions(config.GetCertificatesDir()) // Launch main loop in a goroutine go loop() // SetupSystray is the main thread - configDir := getDefaultConfigDir() + configDir := config.GetDefaultConfigDir() Systray = systray.Systray{ Hibernate: *hibernate, Version: version + "-" + commit, @@ -167,7 +168,7 @@ func loop() { // Instantiate Tools Tools = tools.Tools{ - Directory: getDataDir().String(), + Directory: config.GetDataDir().String(), IndexURL: *indexURL, Logger: func(msg string) { mapD := map[string]string{"DownloadStatus": "Pending", "Msg": msg} @@ -178,7 +179,7 @@ func loop() { Tools.Init(requiredToolsAPILevel) // Let's handle the config - configDir := getDefaultConfigDir() + configDir := config.GetDefaultConfigDir() var configPath *paths.Path // see if the env var is defined, if it is take the config from there, this will override the default path @@ -207,7 +208,7 @@ func loop() { } } if configPath == nil { - configPath = generateConfig(configDir) + configPath = config.GenerateConfig(configDir) } // Parse the config.ini @@ -316,7 +317,7 @@ func loop() { if *crashreport { logFilename := "crashreport_" + time.Now().Format("20060102150405") + ".log" // handle logs directory creation - logsDir := getLogsDir() + logsDir := config.GetLogsDir() logFile, err := os.OpenFile(logsDir.Join(logFilename).String(), os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0644) if err != nil { log.Print("Cannot create file used for crash-report") @@ -376,12 +377,12 @@ func loop() { r.POST("/update", updateHandler) // Mount goa handlers - goa := v2.Server(getDataDir().String()) + goa := v2.Server(config.GetDataDir().String()) r.Any("/v2/*path", gin.WrapH(goa)) go func() { // check if certificates exist; if not, use plain http - certsDir := getCertificatesDir() + certsDir := config.GetCertificatesDir() if certsDir.Join("cert.pem").NotExist() { log.Error("Could not find HTTPS certificate. Using plain HTTP only.") return diff --git a/systray/systray_real.go b/systray/systray_real.go index 106306542..d0a910d80 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -21,12 +21,11 @@ package systray import ( "os" - "os/user" log "github.com/sirupsen/logrus" + "github.com/arduino/arduino-create-agent/config" "github.com/arduino/arduino-create-agent/icon" - "github.com/arduino/go-paths-helper" "github.com/getlantern/systray" "github.com/go-ini/ini" "github.com/skratchdot/open-golang/open" @@ -104,13 +103,13 @@ func (s *Systray) updateMenuItem(item *systray.MenuItem, disable bool) { // CrashesIsEmpty checks if the folder containing crash-reports is empty func (s *Systray) CrashesIsEmpty() bool { - logsDir := getLogsDir() + logsDir := config.GetLogsDir() return logsDir.NotExist() // if the logs directory is empty we assume there are no crashreports } // RemoveCrashes removes the crash-reports from `logs` folder func (s *Systray) RemoveCrashes() { - logsDir := getLogsDir() + logsDir := config.GetLogsDir() pathErr := logsDir.RemoveAll() if pathErr != nil { log.Errorf("Cannot remove crashreports: %s", pathErr) @@ -119,14 +118,6 @@ func (s *Systray) RemoveCrashes() { } } -// getLogsDir simply returns the folder containing the logs -func getLogsDir() *paths.Path { - usr, _ := user.Current() - usrDir := paths.New(usr.HomeDir) // The user folder, on linux/macos /home// - agentDir := usrDir.Join(".arduino-create") - return agentDir.Join("logs") -} - // starthibernate creates a systray icon with menu options to resume/quit the agent func (s *Systray) startHibernate() { systray.SetIcon(icon.GetIconHiber()) From a5bfc94301c9f1bb603a6dcb68f5310633a270d0 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 30 Mar 2023 11:27:48 +0200 Subject: [PATCH 02/22] move `config.ini` in the right package and remove hack --- .gitignore | 2 -- config/config.go | 3 --- config.ini => config/config.ini | 0 3 files changed, 5 deletions(-) rename config.ini => config/config.ini (100%) diff --git a/.gitignore b/.gitignore index 1f0bc1f3c..97789fc52 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,6 @@ public/ artifacts* logs/ -config/config.ini - # IDEs config .idea .vscode diff --git a/config/config.go b/config/config.go index 0a1722953..8dd6552d2 100644 --- a/config/config.go +++ b/config/config.go @@ -79,9 +79,6 @@ func GetDefaultConfigDir() *paths.Path { return agentConfigDir } -// https://github.com/golang/go/issues/46056 -// -//go:generate cp -r ../config.ini config.ini //go:embed config.ini var configContent []byte diff --git a/config.ini b/config/config.ini similarity index 100% rename from config.ini rename to config/config.ini From 2ce9a06414aee1caaccd159a9cd56a8597a720d4 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 30 Mar 2023 17:58:45 +0200 Subject: [PATCH 03/22] fix linter --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 8dd6552d2..657dd0d58 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,7 @@ package config import ( + // we need this for the config ini in this package _ "embed" "os" From ac361f78356b30c76512f3d8b903a0ed99ad74a3 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 23 Mar 2023 15:41:21 +0100 Subject: [PATCH 04/22] move `CrashesIsEmpty` function to the config package and rename it --- config/config.go | 5 +++++ systray/systray_real.go | 10 ++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/config/config.go b/config/config.go index 657dd0d58..4ee3080b0 100644 --- a/config/config.go +++ b/config/config.go @@ -51,6 +51,11 @@ func GetLogsDir() *paths.Path { return logsDir } +// LogsIsEmpty checks if the folder containing crash-reports is empty +func LogsIsEmpty() bool { + return GetLogsDir().NotExist() // if the logs directory is empty we assume there are no crashreports +} + // GetDefaultConfigDir returns the full path to the default Arduino Create Agent configuration directory. func GetDefaultConfigDir() *paths.Path { // UserConfigDir returns the default root directory to use diff --git a/systray/systray_real.go b/systray/systray_real.go index d0a910d80..8d16aae4b 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -60,7 +60,7 @@ func (s *Systray) start() { // Remove crash-reports mRmCrashes := systray.AddMenuItem("Remove crash reports", "") - s.updateMenuItem(mRmCrashes, s.CrashesIsEmpty()) + s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) // Add pause/quit mPause := systray.AddMenuItem("Pause Agent", "") @@ -82,7 +82,7 @@ func (s *Systray) start() { _ = open.Start(s.currentConfigFilePath.String()) case <-mRmCrashes.ClickedCh: s.RemoveCrashes() - s.updateMenuItem(mRmCrashes, s.CrashesIsEmpty()) + s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) case <-mPause.ClickedCh: s.Pause() case <-mQuit.ClickedCh: @@ -101,12 +101,6 @@ func (s *Systray) updateMenuItem(item *systray.MenuItem, disable bool) { } } -// CrashesIsEmpty checks if the folder containing crash-reports is empty -func (s *Systray) CrashesIsEmpty() bool { - logsDir := config.GetLogsDir() - return logsDir.NotExist() // if the logs directory is empty we assume there are no crashreports -} - // RemoveCrashes removes the crash-reports from `logs` folder func (s *Systray) RemoveCrashes() { logsDir := config.GetLogsDir() From 02eacd1a0e01af0895543dba6da77bbb108a1de2 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 23 Mar 2023 17:14:05 +0100 Subject: [PATCH 05/22] move `certificate.go` in it's own package and make functions public --- certificates.go => certificates/certificates.go | 15 +++++++++------ hub.go | 4 +++- main.go | 9 +++++---- 3 files changed, 17 insertions(+), 11 deletions(-) rename certificates.go => certificates/certificates.go (98%) diff --git a/certificates.go b/certificates/certificates.go similarity index 98% rename from certificates.go rename to certificates/certificates.go index 9bb94d59a..16ae3dfce 100644 --- a/certificates.go +++ b/certificates/certificates.go @@ -5,7 +5,7 @@ // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. -package main +package certificates import ( "crypto/ecdsa" @@ -134,10 +134,10 @@ func generateSingleCertificate(isCa bool) (*x509.Certificate, error) { return &template, nil } -// migrateCertificatesGeneratedWithOldAgentVersions checks if certificates generated +// MigrateCertificatesGeneratedWithOldAgentVersions checks if certificates generated // with an old version of the Agent needs to be migrated to the current certificates // directory, and performs the migration if needed. -func migrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { +func MigrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { if certsDir.Join("ca.cert.pem").Exist() { // The new certificates are already set-up, nothing to do return @@ -161,7 +161,8 @@ func migrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { } } -func generateCertificates(certsDir *paths.Path) { +// GenerateCertificates will generate the required certificates useful for a HTTPS connection on localhost +func GenerateCertificates(certsDir *paths.Path) { certsDir.Join("ca.cert.pem").Remove() certsDir.Join("ca.key.pem").Remove() certsDir.Join("cert.pem").Remove() @@ -260,7 +261,8 @@ func generateCertificates(certsDir *paths.Path) { } } -func certHandler(c *gin.Context) { +// CertHandler will expone the certificate (we do not know why this was required) +func CertHandler(c *gin.Context) { if strings.Contains(c.Request.UserAgent(), "Firefox") { c.Header("content-type", "application/x-x509-ca-cert") c.File("ca.cert.cer") @@ -271,7 +273,8 @@ func certHandler(c *gin.Context) { }) } -func deleteCertHandler(c *gin.Context) { +// DeleteCertHandler will delete the certificates +func DeleteCertHandler(c *gin.Context) { DeleteCertificates(config.GetCertificatesDir()) } diff --git a/hub.go b/hub.go index eae24328c..003d3d959 100755 --- a/hub.go +++ b/hub.go @@ -26,7 +26,9 @@ import ( "strconv" "strings" + cert "github.com/arduino/arduino-create-agent/certificates" "github.com/arduino/arduino-create-agent/config" + "github.com/arduino/arduino-create-agent/upload" log "github.com/sirupsen/logrus" ) @@ -183,7 +185,7 @@ func checkCmd(m []byte) { } else if strings.HasPrefix(sl, "downloadtool") { // Always delete root certificates when we receive a downloadtool command // Useful if the install procedure was not followed strictly (eg. manually) - DeleteCertificates(config.GetCertificatesDir()) + cert.DeleteCertificates(config.GetCertificatesDir()) go func() { args := strings.Split(s, " ") var tool, toolVersion, pack, behaviour string diff --git a/main.go b/main.go index f2c4cd8fb..02757db82 100755 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( "time" cors "github.com/andela/gin-cors" + cert "github.com/arduino/arduino-create-agent/certificates" "github.com/arduino/arduino-create-agent/config" "github.com/arduino/arduino-create-agent/systray" "github.com/arduino/arduino-create-agent/tools" @@ -128,11 +129,11 @@ func main() { // Generate certificates if *genCert { - generateCertificates(config.GetCertificatesDir()) + cert.GenerateCertificates(config.GetCertificatesDir()) os.Exit(0) } // Check if certificates made with Agent <=1.2.7 needs to be moved over the new location - migrateCertificatesGeneratedWithOldAgentVersions(config.GetCertificatesDir()) + cert.MigrateCertificatesGeneratedWithOldAgentVersions(config.GetCertificatesDir()) // Launch main loop in a goroutine go loop() @@ -364,8 +365,8 @@ func loop() { r.LoadHTMLFiles("templates/nofirefox.html") r.GET("/", homeHandler) - r.GET("/certificate.crt", certHandler) - r.DELETE("/certificate.crt", deleteCertHandler) + r.GET("/certificate.crt", cert.CertHandler) + r.DELETE("/certificate.crt", cert.DeleteCertHandler) r.POST("/upload", uploadHandler) r.GET("/socket.io/", socketHandler) r.POST("/socket.io/", socketHandler) From 14dfc2b8b2d789d408158ae25be7dc92bd92a9f8 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 31 Mar 2023 18:57:07 +0200 Subject: [PATCH 06/22] update license header --- certificates/certificates.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/certificates/certificates.go b/certificates/certificates.go index 16ae3dfce..0fe44f5bc 100644 --- a/certificates/certificates.go +++ b/certificates/certificates.go @@ -1,6 +1,17 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . // Generate a self-signed X.509 certificate for a TLS server. Outputs to // 'cert.pem' and 'key.pem' and will overwrite existing files. From f7d9772547fcbb27098f36bd72a0426efa6143f9 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Tue, 28 Mar 2023 12:59:40 +0200 Subject: [PATCH 07/22] add menu option to generate the certs only if they are not present --- config/config.go | 6 ++++++ systray/systray_real.go | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/config/config.go b/config/config.go index 4ee3080b0..303aadce8 100644 --- a/config/config.go +++ b/config/config.go @@ -29,6 +29,12 @@ func GetCertificatesDir() *paths.Path { return GetDataDir() } +// CertsExist checks if the certs have already been generated +func CertsExist() bool { + certFile := GetCertificatesDir().Join("cert.pem") + return certFile.Exist() //if the certFile is not present we assume there are no certs +} + // GetDataDir returns the full path to the default Arduino Create Agent data directory. func GetDataDir() *paths.Path { userDir, err := os.UserHomeDir() diff --git a/systray/systray_real.go b/systray/systray_real.go index 8d16aae4b..d0a2d84b2 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -24,7 +24,9 @@ import ( log "github.com/sirupsen/logrus" + cert "github.com/arduino/arduino-create-agent/certificates" "github.com/arduino/arduino-create-agent/config" + "github.com/arduino/arduino-create-agent/icon" "github.com/getlantern/systray" "github.com/go-ini/ini" @@ -62,6 +64,9 @@ func (s *Systray) start() { mRmCrashes := systray.AddMenuItem("Remove crash reports", "") s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) + mGenCerts := systray.AddMenuItem("Generate HTTPS certificates", "HTTPS Certs") + s.updateMenuItem(mGenCerts, config.CertsExist()) + // Add pause/quit mPause := systray.AddMenuItem("Pause Agent", "") systray.AddSeparator() @@ -83,6 +88,9 @@ func (s *Systray) start() { case <-mRmCrashes.ClickedCh: s.RemoveCrashes() s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) + case <-mGenCerts.ClickedCh: + cert.GenerateCertificates(config.GetCertificatesDir()) + s.Restart() case <-mPause.ClickedCh: s.Pause() case <-mQuit.ClickedCh: From 1971f9ee01b6301605f1184341efe8deb577cf18 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Tue, 28 Mar 2023 13:00:15 +0200 Subject: [PATCH 08/22] systray is useless in this func --- systray/systray_real.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/systray/systray_real.go b/systray/systray_real.go index d0a2d84b2..398d45239 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -86,7 +86,7 @@ func (s *Systray) start() { case <-mConfig.ClickedCh: _ = open.Start(s.currentConfigFilePath.String()) case <-mRmCrashes.ClickedCh: - s.RemoveCrashes() + RemoveCrashes() s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) case <-mGenCerts.ClickedCh: cert.GenerateCertificates(config.GetCertificatesDir()) @@ -110,7 +110,7 @@ func (s *Systray) updateMenuItem(item *systray.MenuItem, disable bool) { } // RemoveCrashes removes the crash-reports from `logs` folder -func (s *Systray) RemoveCrashes() { +func RemoveCrashes() { logsDir := config.GetLogsDir() pathErr := logsDir.RemoveAll() if pathErr != nil { From e83c5571709e378a0cdf3f93719dca1fca7c3c83 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 30 Mar 2023 11:29:31 +0200 Subject: [PATCH 09/22] add cert install on macos leverage objective C APIs to install our CA certificate in the user's trusted keystore --- certificates/install_darwin.go | 69 +++++++++++++++++++++++++++++++++ certificates/install_default.go | 29 ++++++++++++++ systray/systray_real.go | 3 +- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 certificates/install_darwin.go create mode 100644 certificates/install_default.go diff --git a/certificates/install_darwin.go b/certificates/install_darwin.go new file mode 100644 index 000000000..a31264239 --- /dev/null +++ b/certificates/install_darwin.go @@ -0,0 +1,69 @@ +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package certificates + +//inspired by https://stackoverflow.com/questions/12798950/ios-install-ssl-certificate-programmatically + +/* +#cgo CFLAGS: -x objective-c +#cgo LDFLAGS: -framework Cocoa +#import + +void installCert(const char *path) { + NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO]; + NSData *rootCertData = [NSData dataWithContentsOfURL:url]; + + OSStatus err = noErr; + SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData); + + CFTypeRef result; + + NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: + (id)kSecClassCertificate, kSecClass, + rootCert, kSecValueRef, + nil]; + + err = SecItemAdd((CFDictionaryRef)dict, &result); + + if( err == noErr) { + NSLog(@"Install root certificate success"); + } else if( err == errSecDuplicateItem ) { + NSLog(@"duplicate root certificate entry"); + } else { + NSLog(@"install root certificate failure"); + } + + NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]}; + err = SecTrustSettingsSetTrustSettings(rootCert, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(newTrustSettings)); + if (err != errSecSuccess) { + NSLog(@"Could not change the trust setting for a certificate. Error: %d", err); + exit(0); + } +} + +*/ +import "C" +import ( + log "github.com/sirupsen/logrus" + + "github.com/arduino/go-paths-helper" +) + +// InstallCertificate will install the certificates in the system keychain on macos +func InstallCertificate(cert *paths.Path) { + log.Infof("Installing certificate: %s", cert) + C.installCert(C.CString(cert.String())) +} diff --git a/certificates/install_default.go b/certificates/install_default.go new file mode 100644 index 000000000..0a330fbf7 --- /dev/null +++ b/certificates/install_default.go @@ -0,0 +1,29 @@ +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +//go:build !darwin + +package certificates + +import ( + log "github.com/sirupsen/logrus" + + "github.com/arduino/go-paths-helper" +) + +// InstallCertificate won't do anything on unsupported Operative Systems +func InstallCertificate(cert *paths.Path) { + log.Warn("platform not supported for the certificate install") +} diff --git a/systray/systray_real.go b/systray/systray_real.go index 398d45239..b4af1ac6b 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -64,7 +64,7 @@ func (s *Systray) start() { mRmCrashes := systray.AddMenuItem("Remove crash reports", "") s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) - mGenCerts := systray.AddMenuItem("Generate HTTPS certificates", "HTTPS Certs") + mGenCerts := systray.AddMenuItem("Generate and Install HTTPS certificates", "HTTPS Certs") s.updateMenuItem(mGenCerts, config.CertsExist()) // Add pause/quit @@ -90,6 +90,7 @@ func (s *Systray) start() { s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) case <-mGenCerts.ClickedCh: cert.GenerateCertificates(config.GetCertificatesDir()) + cert.InstallCertificate(config.GetCertificatesDir().Join("ca.cert.cer")) s.Restart() case <-mPause.ClickedCh: s.Pause() From bcaeb75adcc1c2e0a0e9df916fca0740d93da17e Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 7 Apr 2023 16:10:03 +0200 Subject: [PATCH 10/22] add cert install on win leverage windows syscalls to install our CA certificate in the user's trusted keystore code heavily inspired by https://github.com/FiloSottile/mkcert --- certificates/install_default.go | 2 +- certificates/install_windows.go | 104 ++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 certificates/install_windows.go diff --git a/certificates/install_default.go b/certificates/install_default.go index 0a330fbf7..1c0003191 100644 --- a/certificates/install_default.go +++ b/certificates/install_default.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build !darwin +//go:build !darwin && !windows package certificates diff --git a/certificates/install_windows.go b/certificates/install_windows.go new file mode 100644 index 000000000..3975be643 --- /dev/null +++ b/certificates/install_windows.go @@ -0,0 +1,104 @@ +// Copyright 2023 Arduino SA +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// code inspired by https://github.com/FiloSottile/mkcert licenced under BSD3 + +package certificates + +import ( + "encoding/pem" + "fmt" + "io/ioutil" + "syscall" + "unsafe" + + "github.com/arduino/go-paths-helper" + log "github.com/sirupsen/logrus" +) + +var ( + modcrypt32 = syscall.NewLazyDLL("crypt32.dll") + procCertAddEncodedCertificateToStore = modcrypt32.NewProc("CertAddEncodedCertificateToStore") + procCertCloseStore = modcrypt32.NewProc("CertCloseStore") + procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW") +) + +// // InstallCertificate will install the certificates in the system keychain on windows +func InstallCertificate(certFile *paths.Path) { + // Load cert + cert, err := ioutil.ReadFile(certFile.String()) + if err != nil { + log.Errorf("failed to read root certificate: %s", err) + } + // Decode PEM + if certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != "CERTIFICATE" { + log.Error("invalid PEM data: decode pem") + } else { + cert = certBlock.Bytes + } + // Open root store + store, err := openWindowsRootStore() + if err != nil { + log.Errorf("cannot open root store %s", err) + } else { + log.Info("opened Root Store") + } + defer store.close() + // Add cert + err = store.addCert(cert) + if err != nil { + log.Errorf("cannot install certificate in the system keychain: %s", err) + } else { + log.Info("certificate installed") + } +} + +type windowsRootStore uintptr + +func openWindowsRootStore() (windowsRootStore, error) { + rootStr, err := syscall.UTF16PtrFromString("ROOT") + if err != nil { + return 0, err + } + store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr))) + if store != 0 { + return windowsRootStore(store), nil + } + return 0, fmt.Errorf("failed to open windows root store: %s", err) +} + +func (w windowsRootStore) close() error { + ret, _, err := procCertCloseStore.Call(uintptr(w), 0) + if ret != 0 { + return nil + } + return fmt.Errorf("failed to close windows root store: %s", err) +} + +func (w windowsRootStore) addCert(cert []byte) error { + // this will always override + ret, _, err := procCertAddEncodedCertificateToStore.Call( + uintptr(w), // HCERTSTORE hCertStore + uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType + uintptr(unsafe.Pointer(&cert[0])), // const BYTE *pbCertEncoded + uintptr(len(cert)), // DWORD cbCertEncoded + 3, // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3) + 0, // PCCERT_CONTEXT *ppCertContext + ) + if ret != 0 { + return nil + } + return fmt.Errorf("failed adding cert: %s", err) +} From 7f436f27d8c80d8c2236a111c3b14f18b88c9444 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 6 Apr 2023 18:13:26 +0200 Subject: [PATCH 11/22] fix certificate not being valid for 127.0.0.1 --- certificates/certificates.go | 10 ++-------- main.go | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/certificates/certificates.go b/certificates/certificates.go index 0fe44f5bc..03a0419be 100644 --- a/certificates/certificates.go +++ b/certificates/certificates.go @@ -127,14 +127,8 @@ func generateSingleCertificate(isCa bool) (*x509.Certificate, error) { BasicConstraintsValid: true, } - hosts := strings.Split(host, ",") - for _, h := range hosts { - if ip := net.ParseIP(h); ip != nil { - template.IPAddresses = append(template.IPAddresses, ip) - } else { - template.DNSNames = append(template.DNSNames, h) - } - } + template.IPAddresses = append(template.IPAddresses, net.ParseIP("127.0.0.1")) + template.DNSNames = append(template.DNSNames, "localhost") if isCa { template.IsCA = true diff --git a/main.go b/main.go index 02757db82..1011ccefe 100755 --- a/main.go +++ b/main.go @@ -350,6 +350,7 @@ func loop() { extraOrigins = append(extraOrigins, "http://localhost:"+port) extraOrigins = append(extraOrigins, "https://localhost:"+port) extraOrigins = append(extraOrigins, "http://127.0.0.1:"+port) + extraOrigins = append(extraOrigins, "https://127.0.0.1:"+port) } r.Use(cors.Middleware(cors.Config{ From 3a5cb38bf95116476e6a5ea223f0e5bbc4736edc Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Wed, 12 Apr 2023 12:12:35 +0200 Subject: [PATCH 12/22] disable the generation/install certs menuItem on OS that are not macos --- systray/systray_real.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/systray/systray_real.go b/systray/systray_real.go index b4af1ac6b..a1542a3f7 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -21,6 +21,7 @@ package systray import ( "os" + "runtime" log "github.com/sirupsen/logrus" @@ -65,7 +66,13 @@ func (s *Systray) start() { s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) mGenCerts := systray.AddMenuItem("Generate and Install HTTPS certificates", "HTTPS Certs") - s.updateMenuItem(mGenCerts, config.CertsExist()) + // On linux/windows chrome/firefox/edge(chromium) the agent works without problems on plain HTTP, + // so we disable the menuItem to generate/install the certificates + if runtime.GOOS != "darwin" { + s.updateMenuItem(mGenCerts, true) + } else { + s.updateMenuItem(mGenCerts, config.CertsExist()) + } // Add pause/quit mPause := systray.AddMenuItem("Pause Agent", "") From b62a2ff745c804f12ea1dcb6d8856602d5aa8545 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 14 Apr 2023 11:09:12 +0200 Subject: [PATCH 13/22] remove the cert generation from the installer, remove duplicate step --- .github/workflows/release.yml | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65a01c868..f01a1d47d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -334,9 +334,6 @@ jobs: INSTALLBUILDER_PATH: "/opt/installbuilder-22.10.0/bin/builder" INSTALLER_VARS: "project.outputDirectory=$PWD project.version=${GITHUB_REF##*/} workspace=$PWD realname=Arduino_Create_Agent" # vars passed to installbuilder to install https certs automatically - CERT_INSTALL: "ask_certificates_install=CI" # win(edge),mac(safari) - NO_CERT_INSTALL: "ask_certificates_install=CS" # linux - CHOICE_CERT_INSTALL: "ask_certificates_install=CC" # win,mac:(ff,chrome) CREATE_OSX_BUNDLED_MG: 0 # tell installbuilder to not create the DMG, gon will take care of that later # installbuilder will read this vars automatically (defined in installer.xml): INSTALLER_CERT_WINDOWS_PASSWORD: ${{ secrets.INSTALLER_CERT_WINDOWS_PASSWORD }} @@ -353,24 +350,22 @@ jobs: - os: ubuntu-20.04 install-builder-name: linux executable-path: artifacts/linux-amd64/ + installer-extension: .tar.gz artifact-name: arduino-create-agent-ubuntu-20.04-amd64 - os: windows-2019 arch: -386 - browser: edge install-builder-name: windows executable-path: artifacts/windows/ extension: .exe installer-extension: .exe artifact-name: arduino-create-agent-windows-2019-386 - os: windows-2019 - browser: edge install-builder-name: windows executable-path: artifacts/windows/ extension: .exe installer-extension: .exe artifact-name: arduino-create-agent-windows-2019-amd64 - os: macos-12 - browser: safari install-builder-name: osx executable-path: artifacts/macos/ArduinoCreateAgent.app installer-extension: .app @@ -425,27 +420,11 @@ jobs: run: echo "${{ secrets.INSTALLER_CERT_MAC_P12 }}" | base64 --decode > ${{ env.INSTALLER_CERT_MAC_P12 }} if: matrix.os == 'macos-12' - # win(edge),mac(safari) -> CERT_INSTALL and win,mac:(ff,chrome) -> CHOICE_CERT_INSTALL # installbuilder reads the env vars with certs paths and use it to sign the installer. - - name: Launch Bitrock installbuilder-20 with CERT_INSTALL && CHOICE_CERT_INSTALL + - name: Launch Bitrock installbuilder run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.CERT_INSTALL }} - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CI${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-${{matrix.browser}}${{matrix.installer-extension}} - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.CHOICE_CERT_INSTALL }} - cp -vr ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CC${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome${{matrix.installer-extension}} - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-CC${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox${{matrix.installer-extension}} - rm -r ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer-C* - if: matrix.os == 'windows-2019' || matrix.os == 'macos-12' - - # linux - - name: Launch Bitrock installbuilder-20 with NO_CERT_INSTALL - run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml linux-x64 --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} ${{ env.NO_CERT_INSTALL }} - cp -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.run ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome.run - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.run ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox.run - cp -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-chrome.tar.gz - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-linux-x64-installer-CS.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer-firefox.tar.gz - if: matrix.os == 'ubuntu-20.04' + ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} + mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer${{matrix.installer-extension}} - name: Upload artifacts uses: actions/upload-artifact@v3 From 2b41a4d8d27ab64f18a503840f17ef21e11f3fd8 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 14 Apr 2023 16:06:11 +0200 Subject: [PATCH 14/22] parallelization for notarization is no more required --- .github/workflows/release.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f01a1d47d..3fae35e76 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -438,7 +438,6 @@ jobs: needs: package strategy: matrix: - browser: [safari, firefox, chrome] arch: [-amd64] runs-on: macos-12 @@ -451,7 +450,7 @@ jobs: # zip artifacts do not mantain executable permission - name: Make executable - run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx${{ matrix.arch }}-installer-${{ matrix.browser }}.app/Contents/MacOS/* + run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx${{ matrix.arch }}-installer.app/Contents/MacOS/* - name: Import Code-Signing Certificates run: | @@ -481,7 +480,7 @@ jobs: # gon does not allow env variables in config file (https://github.com/mitchellh/gon/issues/20) run: | cat > gon.config_installer.hcl < Date: Fri, 14 Apr 2023 16:10:47 +0200 Subject: [PATCH 15/22] test new version of the installer config --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fae35e76..665f4dccc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -380,6 +380,7 @@ jobs: uses: actions/checkout@v3 with: repository: 'bcmi-labs/arduino-create-agent-installer' # the repo which contains install.xml + ref: certs token: ${{ secrets.ARDUINO_CREATE_AGENT_CI_PAT }} - name: Download artifact From 3e7feadc3e34230a7124db3f10278597e8f4a50f Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 17 Apr 2023 15:24:16 +0200 Subject: [PATCH 16/22] move archive generation in the CI and save a rename operation (we do that in the installer) --- .github/workflows/release.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 665f4dccc..de2aa28f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -350,7 +350,7 @@ jobs: - os: ubuntu-20.04 install-builder-name: linux executable-path: artifacts/linux-amd64/ - installer-extension: .tar.gz + installer-extension: .run artifact-name: arduino-create-agent-ubuntu-20.04-amd64 - os: windows-2019 arch: -386 @@ -424,8 +424,11 @@ jobs: # installbuilder reads the env vars with certs paths and use it to sign the installer. - name: Launch Bitrock installbuilder run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} - mv -v ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-installer${{matrix.installer-extension}} ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer${{matrix.installer-extension}} + ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars "${{ env.INSTALLER_VARS }} architecture=${{ matrix.arch }}" + + - name: Generate archive + run: tar -czvf ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer${{matrix.installer-extension}} + if: matrix.os == 'ubuntu-20.04' - name: Upload artifacts uses: actions/upload-artifact@v3 From 0b35ff42300a84c4a0a510e6e289fc732ebda064 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 17 Apr 2023 15:26:38 +0200 Subject: [PATCH 17/22] removed `-` in arch matrix variable for clarity --- .github/workflows/release.yml | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de2aa28f8..e64446745 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,10 +31,10 @@ jobs: strategy: matrix: os: [ubuntu-20.04, windows-2019, macos-12] - arch: [-amd64] + arch: [amd64] include: - os: windows-2019 - arch: -386 + arch: 386 ext: ".exe" - os: windows-2019 ext: ".exe" @@ -104,11 +104,11 @@ jobs: env: GOARCH: 386 # 32bit architecture (for support) run: task go:build-win - if: matrix.os == 'windows-2019' && matrix.arch == '-386' + if: matrix.os == 'windows-2019' && matrix.arch == '386' - name: Build the Agent for win64 run: task go:build-win # GOARCH=amd64 by default on the runners - if: matrix.os == 'windows-2019' && matrix.arch == '-amd64' + if: matrix.os == 'windows-2019' && matrix.arch == 'amd64' - name: Build the Agent for macos env: @@ -121,7 +121,7 @@ jobs: # this will create `public/` dir with compressed full bin (/-.gz) and a json file - name: Create autoupdate files run: go-selfupdate ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} - if: matrix.arch != '-386' && steps.prerelease.outputs.IS_PRE != 'true' + if: matrix.arch != '386' && steps.prerelease.outputs.IS_PRE != 'true' # for now we do not distribute m1 build, this is a workaround for now - name: Copy autoupdate file for darwin-arm64 (m1 arch) @@ -132,8 +132,8 @@ jobs: if: matrix.os == 'macos-12' && steps.prerelease.outputs.IS_PRE != 'true' - name: Create autoupdate files for win32 - run: go-selfupdate -platform windows${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} - if: matrix.arch == '-386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true' + run: go-selfupdate -platform windows-${{ matrix.arch }} ${{ env.PROJECT_NAME }}${{ matrix.ext }} ${TAG_VERSION} + if: matrix.arch == '386' && matrix.os == 'windows-2019' && steps.prerelease.outputs.IS_PRE != 'true' - name: Upload autoupdate files to Arduino downloads servers run: | @@ -144,7 +144,7 @@ jobs: - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ${{ env.PROJECT_NAME }}-${{ matrix.os }}${{ matrix.arch }} + name: ${{ env.PROJECT_NAME }}-${{ matrix.os }}-${{ matrix.arch }} path: | ${{ env.PROJECT_NAME }}* if-no-files-found: error @@ -345,7 +345,7 @@ jobs: fail-fast: false # if one os is failing continue nonetheless matrix: # used to generate installers for different OS and not for runs-on os: [ubuntu-20.04, windows-2019, macos-12] - arch: [-amd64] + arch: [amd64] include: - os: ubuntu-20.04 install-builder-name: linux @@ -353,7 +353,7 @@ jobs: installer-extension: .run artifact-name: arduino-create-agent-ubuntu-20.04-amd64 - os: windows-2019 - arch: -386 + arch: 386 install-builder-name: windows executable-path: artifacts/windows/ extension: .exe @@ -424,16 +424,16 @@ jobs: # installbuilder reads the env vars with certs paths and use it to sign the installer. - name: Launch Bitrock installbuilder run: | - ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars "${{ env.INSTALLER_VARS }} architecture=${{ matrix.arch }}" + ${{ env.INSTALLBUILDER_PATH }} build installer.xml ${{ matrix.install-builder-name }} --verbose --license /tmp/license.xml --setvars ${{ env.INSTALLER_VARS }} architecture=${{ matrix.arch }} - name: Generate archive - run: tar -czvf ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}${{ matrix.arch }}-installer${{matrix.installer-extension}} + run: tar -czvf ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-${{ matrix.arch }}-installer.tar.gz ArduinoCreateAgent-${GITHUB_REF##*/}-${{ matrix.install-builder-name }}-${{ matrix.arch }}-installer${{matrix.installer-extension}} if: matrix.os == 'ubuntu-20.04' - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ArduinoCreateAgent-${{ matrix.install-builder-name }}${{ matrix.arch }} + name: ArduinoCreateAgent-${{ matrix.install-builder-name }}-${{ matrix.arch }} path: ArduinoCreateAgent* if-no-files-found: error @@ -442,19 +442,19 @@ jobs: needs: package strategy: matrix: - arch: [-amd64] + arch: [amd64] runs-on: macos-12 steps: - name: Download artifact uses: actions/download-artifact@v3 with: - name: ArduinoCreateAgent-osx${{ matrix.arch }} + name: ArduinoCreateAgent-osx-${{ matrix.arch }} path: ArduinoCreateAgent-osx # zip artifacts do not mantain executable permission - name: Make executable - run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx${{ matrix.arch }}-installer.app/Contents/MacOS/* + run: chmod -v +x ArduinoCreateAgent-osx/ArduinoCreateAgent-${GITHUB_REF##*/}-osx-${{ matrix.arch }}-installer.app/Contents/MacOS/* - name: Import Code-Signing Certificates run: | @@ -484,7 +484,7 @@ jobs: # gon does not allow env variables in config file (https://github.com/mitchellh/gon/issues/20) run: | cat > gon.config_installer.hcl < Date: Wed, 19 Apr 2023 14:52:04 +0200 Subject: [PATCH 18/22] Revert "add cert install on win" This reverts commit bcaeb75adcc1c2e0a0e9df916fca0740d93da17e. --- certificates/install_default.go | 2 +- certificates/install_windows.go | 104 -------------------------------- 2 files changed, 1 insertion(+), 105 deletions(-) delete mode 100644 certificates/install_windows.go diff --git a/certificates/install_default.go b/certificates/install_default.go index 1c0003191..0a330fbf7 100644 --- a/certificates/install_default.go +++ b/certificates/install_default.go @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -//go:build !darwin && !windows +//go:build !darwin package certificates diff --git a/certificates/install_windows.go b/certificates/install_windows.go deleted file mode 100644 index 3975be643..000000000 --- a/certificates/install_windows.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2023 Arduino SA -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -// code inspired by https://github.com/FiloSottile/mkcert licenced under BSD3 - -package certificates - -import ( - "encoding/pem" - "fmt" - "io/ioutil" - "syscall" - "unsafe" - - "github.com/arduino/go-paths-helper" - log "github.com/sirupsen/logrus" -) - -var ( - modcrypt32 = syscall.NewLazyDLL("crypt32.dll") - procCertAddEncodedCertificateToStore = modcrypt32.NewProc("CertAddEncodedCertificateToStore") - procCertCloseStore = modcrypt32.NewProc("CertCloseStore") - procCertOpenSystemStoreW = modcrypt32.NewProc("CertOpenSystemStoreW") -) - -// // InstallCertificate will install the certificates in the system keychain on windows -func InstallCertificate(certFile *paths.Path) { - // Load cert - cert, err := ioutil.ReadFile(certFile.String()) - if err != nil { - log.Errorf("failed to read root certificate: %s", err) - } - // Decode PEM - if certBlock, _ := pem.Decode(cert); certBlock == nil || certBlock.Type != "CERTIFICATE" { - log.Error("invalid PEM data: decode pem") - } else { - cert = certBlock.Bytes - } - // Open root store - store, err := openWindowsRootStore() - if err != nil { - log.Errorf("cannot open root store %s", err) - } else { - log.Info("opened Root Store") - } - defer store.close() - // Add cert - err = store.addCert(cert) - if err != nil { - log.Errorf("cannot install certificate in the system keychain: %s", err) - } else { - log.Info("certificate installed") - } -} - -type windowsRootStore uintptr - -func openWindowsRootStore() (windowsRootStore, error) { - rootStr, err := syscall.UTF16PtrFromString("ROOT") - if err != nil { - return 0, err - } - store, _, err := procCertOpenSystemStoreW.Call(0, uintptr(unsafe.Pointer(rootStr))) - if store != 0 { - return windowsRootStore(store), nil - } - return 0, fmt.Errorf("failed to open windows root store: %s", err) -} - -func (w windowsRootStore) close() error { - ret, _, err := procCertCloseStore.Call(uintptr(w), 0) - if ret != 0 { - return nil - } - return fmt.Errorf("failed to close windows root store: %s", err) -} - -func (w windowsRootStore) addCert(cert []byte) error { - // this will always override - ret, _, err := procCertAddEncodedCertificateToStore.Call( - uintptr(w), // HCERTSTORE hCertStore - uintptr(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING), // DWORD dwCertEncodingType - uintptr(unsafe.Pointer(&cert[0])), // const BYTE *pbCertEncoded - uintptr(len(cert)), // DWORD cbCertEncoded - 3, // DWORD dwAddDisposition (CERT_STORE_ADD_REPLACE_EXISTING is 3) - 0, // PCCERT_CONTEXT *ppCertContext - ) - if ret != 0 { - return nil - } - return fmt.Errorf("failed adding cert: %s", err) -} From 80071071ddbc9a1bfbe901a519aabded016839cb Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Thu, 20 Apr 2023 12:26:21 +0200 Subject: [PATCH 19/22] add popup on error --- certificates/install_darwin.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/certificates/install_darwin.go b/certificates/install_darwin.go index a31264239..571a8ea26 100644 --- a/certificates/install_darwin.go +++ b/certificates/install_darwin.go @@ -22,11 +22,13 @@ package certificates #cgo LDFLAGS: -framework Cocoa #import -void installCert(const char *path) { +char *installCert(const char *path) { NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO]; NSData *rootCertData = [NSData dataWithContentsOfURL:url]; OSStatus err = noErr; + NSMutableString *errString = [NSMutableString new]; + char *errReturnString = "\0"; SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData); CFTypeRef result; @@ -41,22 +43,32 @@ void installCert(const char *path) { if( err == noErr) { NSLog(@"Install root certificate success"); } else if( err == errSecDuplicateItem ) { - NSLog(@"duplicate root certificate entry"); + errString = [@"duplicate root certificate entry. Error: " stringByAppendingFormat:@"%d ",err]; + NSLog(errString); + errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; + return errReturnString; } else { - NSLog(@"install root certificate failure"); + errString = [@"install root certificate failure. Error: " stringByAppendingFormat:@"%d ",err]; + NSLog(errString); + errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; + return errReturnString; } NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]}; err = SecTrustSettingsSetTrustSettings(rootCert, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(newTrustSettings)); if (err != errSecSuccess) { - NSLog(@"Could not change the trust setting for a certificate. Error: %d", err); - exit(0); + errString = [@"Could not change the trust setting for a certificate. Error: " stringByAppendingFormat:@"%d ",err]; + NSLog(errString); + errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; } + return errReturnString; } */ import "C" import ( + "os/exec" + log "github.com/sirupsen/logrus" "github.com/arduino/go-paths-helper" @@ -65,5 +77,11 @@ import ( // InstallCertificate will install the certificates in the system keychain on macos func InstallCertificate(cert *paths.Path) { log.Infof("Installing certificate: %s", cert) - C.installCert(C.CString(cert.String())) + p := C.installCert(C.CString(cert.String())) + s := C.GoString(p) + if len(s) != 0 { + oscmd := exec.Command("osascript", "-e", "display dialog \""+s+"\" buttons \"OK\" with title \"Error installing certificates\"") + _ = oscmd.Run() + log.Info(oscmd.String()) + } } From 4dde91e0732f156cf1d4be60043bb3ec04412729 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Thu, 20 Apr 2023 17:52:18 +0200 Subject: [PATCH 20/22] Fixed warnings in obj-c code --- certificates/install_darwin.go | 37 ++++++++++++++++------------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/certificates/install_darwin.go b/certificates/install_darwin.go index 571a8ea26..e8d042fb2 100644 --- a/certificates/install_darwin.go +++ b/certificates/install_darwin.go @@ -22,46 +22,43 @@ package certificates #cgo LDFLAGS: -framework Cocoa #import -char *installCert(const char *path) { +const char *installCert(const char *path) { NSURL *url = [NSURL fileURLWithPath:@(path) isDirectory:NO]; NSData *rootCertData = [NSData dataWithContentsOfURL:url]; OSStatus err = noErr; - NSMutableString *errString = [NSMutableString new]; - char *errReturnString = "\0"; SecCertificateRef rootCert = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef) rootCertData); CFTypeRef result; NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys: - (id)kSecClassCertificate, kSecClass, - rootCert, kSecValueRef, - nil]; + (id)kSecClassCertificate, kSecClass, + rootCert, kSecValueRef, + nil]; err = SecItemAdd((CFDictionaryRef)dict, &result); - if( err == noErr) { + if (err == noErr) { NSLog(@"Install root certificate success"); - } else if( err == errSecDuplicateItem ) { - errString = [@"duplicate root certificate entry. Error: " stringByAppendingFormat:@"%d ",err]; - NSLog(errString); - errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; - return errReturnString; + } else if (err == errSecDuplicateItem) { + NSString *errString = [@"duplicate root certificate entry. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]];; } else { - errString = [@"install root certificate failure. Error: " stringByAppendingFormat:@"%d ",err]; - NSLog(errString); - errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; - return errReturnString; + NSString *errString = [@"install root certificate failure. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; } NSDictionary *newTrustSettings = @{(id)kSecTrustSettingsResult: [NSNumber numberWithInt:kSecTrustSettingsResultTrustRoot]}; err = SecTrustSettingsSetTrustSettings(rootCert, kSecTrustSettingsDomainUser, (__bridge CFTypeRef)(newTrustSettings)); if (err != errSecSuccess) { - errString = [@"Could not change the trust setting for a certificate. Error: " stringByAppendingFormat:@"%d ",err]; - NSLog(errString); - errReturnString = [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; + NSString *errString = [@"Could not change the trust setting for a certificate. Error: " stringByAppendingFormat:@"%d", err]; + NSLog(@"%@", errString); + return [errString cStringUsingEncoding:[NSString defaultCStringEncoding]]; } - return errReturnString; + + return ""; } */ From 85a7b00854c0dbe92f219686175405df82e6b5dc Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Fri, 21 Apr 2023 13:13:36 +0200 Subject: [PATCH 21/22] remove the certs if the install process errors. The user is able to retry --- certificates/certificates.go | 9 ++++----- certificates/install_darwin.go | 9 ++++++--- certificates/install_default.go | 5 ++++- systray/systray_real.go | 9 +++++++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/certificates/certificates.go b/certificates/certificates.go index 03a0419be..a52599318 100644 --- a/certificates/certificates.go +++ b/certificates/certificates.go @@ -168,10 +168,6 @@ func MigrateCertificatesGeneratedWithOldAgentVersions(certsDir *paths.Path) { // GenerateCertificates will generate the required certificates useful for a HTTPS connection on localhost func GenerateCertificates(certsDir *paths.Path) { - certsDir.Join("ca.cert.pem").Remove() - certsDir.Join("ca.key.pem").Remove() - certsDir.Join("cert.pem").Remove() - certsDir.Join("key.pem").Remove() // Create the key for the certification authority caKey, err := generateKey("P256") @@ -285,9 +281,12 @@ func DeleteCertHandler(c *gin.Context) { // DeleteCertificates will delete the certificates func DeleteCertificates(certDir *paths.Path) { + certDir.Join("ca.key.pem").Remove() certDir.Join("ca.cert.pem").Remove() certDir.Join("ca.cert.cer").Remove() - certDir.Join("ca.key.pem").Remove() + certDir.Join("key.pem").Remove() + certDir.Join("cert.pem").Remove() + certDir.Join("cert.cer").Remove() } const noFirefoxTemplateHTML = ` diff --git a/certificates/install_darwin.go b/certificates/install_darwin.go index e8d042fb2..765257b91 100644 --- a/certificates/install_darwin.go +++ b/certificates/install_darwin.go @@ -64,6 +64,7 @@ const char *installCert(const char *path) { */ import "C" import ( + "errors" "os/exec" log "github.com/sirupsen/logrus" @@ -71,14 +72,16 @@ import ( "github.com/arduino/go-paths-helper" ) -// InstallCertificate will install the certificates in the system keychain on macos -func InstallCertificate(cert *paths.Path) { +// InstallCertificate will install the certificates in the system keychain on macos, +// if something goes wrong will show a dialog with the error and return an error +func InstallCertificate(cert *paths.Path) error { log.Infof("Installing certificate: %s", cert) p := C.installCert(C.CString(cert.String())) s := C.GoString(p) if len(s) != 0 { oscmd := exec.Command("osascript", "-e", "display dialog \""+s+"\" buttons \"OK\" with title \"Error installing certificates\"") _ = oscmd.Run() - log.Info(oscmd.String()) + return errors.New(s) } + return nil } diff --git a/certificates/install_default.go b/certificates/install_default.go index 0a330fbf7..2a1cf794f 100644 --- a/certificates/install_default.go +++ b/certificates/install_default.go @@ -18,12 +18,15 @@ package certificates import ( + "errors" + log "github.com/sirupsen/logrus" "github.com/arduino/go-paths-helper" ) // InstallCertificate won't do anything on unsupported Operative Systems -func InstallCertificate(cert *paths.Path) { +func InstallCertificate(cert *paths.Path) error { log.Warn("platform not supported for the certificate install") + return errors.New("platform not supported for the certificate install") } diff --git a/systray/systray_real.go b/systray/systray_real.go index a1542a3f7..74ed12c67 100644 --- a/systray/systray_real.go +++ b/systray/systray_real.go @@ -96,8 +96,13 @@ func (s *Systray) start() { RemoveCrashes() s.updateMenuItem(mRmCrashes, config.LogsIsEmpty()) case <-mGenCerts.ClickedCh: - cert.GenerateCertificates(config.GetCertificatesDir()) - cert.InstallCertificate(config.GetCertificatesDir().Join("ca.cert.cer")) + certDir := config.GetCertificatesDir() + cert.GenerateCertificates(certDir) + err := cert.InstallCertificate(certDir.Join("ca.cert.cer")) + // if something goes wrong during the cert install we remove them, so the user is able to retry + if err != nil { + cert.DeleteCertificates(certDir) + } s.Restart() case <-mPause.ClickedCh: s.Pause() From bda15746cefec6218f2d02f49fe1619cc2cf6e53 Mon Sep 17 00:00:00 2001 From: Umberto Baldi Date: Mon, 17 Apr 2023 18:09:36 +0200 Subject: [PATCH 22/22] Revert "test new version of the installer config" This reverts commit fa425dda0625069142166f3135b2bdaa355c6ee3. --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e64446745..541da69c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -380,7 +380,6 @@ jobs: uses: actions/checkout@v3 with: repository: 'bcmi-labs/arduino-create-agent-installer' # the repo which contains install.xml - ref: certs token: ${{ secrets.ARDUINO_CREATE_AGENT_CI_PAT }} - name: Download artifact