diff --git a/commander b/commander index 414c252..2a7c11f 100755 --- a/commander +++ b/commander @@ -199,6 +199,9 @@ case $txt in "server-shutdown") halt ;; +"version-update") + /home/labca/labca/install &>>$LOGFILE + ;; *) echo "Unknown command '$txt'. ERROR!" exit 1 @@ -206,8 +209,3 @@ case $txt in esac echo "ok" - - -# TODO: -# upgrade the labca stuff - diff --git a/config_expiration-mailer.patch b/config_expiration-mailer.patch index 3b48747..1d05693 100644 --- a/config_expiration-mailer.patch +++ b/config_expiration-mailer.patch @@ -4,7 +4,7 @@ index 444beae43..e9bd228ef 100644 +++ b/test/config/expiration-mailer.json @@ -12,6 +12,11 @@ "nagCheckInterval": "24h", - "emailTemplate": "labca/example-expiration-template", + "emailTemplate": "test/example-expiration-template", "debugAddr": ":8008", + "dnsTries": 3, + "dnsResolvers": [ @@ -12,8 +12,8 @@ index 444beae43..e9bd228ef 100644 + "127.0.0.1:8054" + ], "tls": { - "caCertFile": "labca/grpc-creds/minica.pem", - "certFile": "labca/grpc-creds/expiration-mailer.boulder/cert.pem", + "caCertFile": "test/grpc-creds/minica.pem", + "certFile": "test/grpc-creds/expiration-mailer.boulder/cert.pem", @@ -28,5 +33,10 @@ "syslog": { "stdoutlevel": 6, diff --git a/docker-compose.patch b/docker-compose.patch index 0b39be9..c60c8ce 100644 --- a/docker-compose.patch +++ b/docker-compose.patch @@ -1,5 +1,5 @@ diff --git a/docker-compose.yml b/docker-compose.yml -index f3279eeab..76573dabe 100644 +index e34704a4d..46365bdcf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: @@ -34,7 +34,7 @@ index f3279eeab..76573dabe 100644 + restart: always bhsm: # To minimize fetching this should be the same version used above - image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2020-01-07 + image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2020-04-08 @@ -68,8 +75,16 @@ services: bluenet: aliases: @@ -64,7 +64,7 @@ index f3279eeab..76573dabe 100644 + max-file: "5" + restart: always + labca: - image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2020-01-07 + image: letsencrypt/boulder-tools-go${TRAVIS_GO_VERSION:-1.13.2}:2020-04-08 - environment: - GO111MODULE: "on" - GOFLAGS: "-mod=vendor" diff --git a/expiration-mailer_main.patch b/expiration-mailer_main.patch index 775eb34..015f6c7 100644 --- a/expiration-mailer_main.patch +++ b/expiration-mailer_main.patch @@ -1,5 +1,5 @@ diff --git a/cmd/expiration-mailer/main.go b/cmd/expiration-mailer/main.go -index 49ce1a265..9d47457b9 100644 +index 6d31a7033..c2ad80495 100644 --- a/cmd/expiration-mailer/main.go +++ b/cmd/expiration-mailer/main.go @@ -19,6 +19,7 @@ import ( @@ -19,7 +19,7 @@ index 49ce1a265..9d47457b9 100644 ) type regStore interface { -@@ -384,6 +385,9 @@ type config struct { +@@ -383,6 +384,9 @@ type config struct { TLS cmd.TLSConfig SAService *cmd.GRPCClientConfig @@ -29,7 +29,7 @@ index 49ce1a265..9d47457b9 100644 // Path to a file containing a list of trusted root certificates for use // during the SMTP connection (as opposed to the gRPC connections). SMTPTrustedRootFile string -@@ -392,6 +396,12 @@ type config struct { +@@ -391,6 +395,12 @@ type config struct { } Syslog cmd.SyslogConfig @@ -42,7 +42,7 @@ index 49ce1a265..9d47457b9 100644 } func initStats(stats prometheus.Registerer) mailerStats { -@@ -495,6 +505,30 @@ func main() { +@@ -494,6 +504,30 @@ func main() { cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA") sac := bgrpc.NewStorageAuthorityClient(sapb.NewStorageAuthorityClient(conn)) @@ -73,7 +73,7 @@ index 49ce1a265..9d47457b9 100644 var smtpRoots *x509.CertPool if c.Mailer.SMTPTrustedRootFile != "" { pem, err := ioutil.ReadFile(c.Mailer.SMTPTrustedRootFile) -@@ -530,6 +564,7 @@ func main() { +@@ -529,6 +563,7 @@ func main() { c.Mailer.Username, smtpPassword, smtpRoots, diff --git a/gui/apply-boulder b/gui/apply-boulder index 096b460..8b92431 100755 --- a/gui/apply-boulder +++ b/gui/apply-boulder @@ -44,6 +44,7 @@ sed -i -e "s/\"server\": \".*\"/\"server\": \"$PKI_EMAIL_SERVER\"/" config/expir sed -i -e "s/\"port\": \".*\"/\"port\": \"$PKI_EMAIL_PORT\"/" config/expiration-mailer.json sed -i -e "s/\"username\": \".*\"/\"username\": \"$PKI_EMAIL_USER\"/" config/expiration-mailer.json sed -i -e "s/\"from\": \".*\"/\"from\": \"$PKI_EMAIL_FROM\"/" config/expiration-mailer.json +sed -i -e "s/\"purgeInterval\": \".*\"/\"purgeInterval\": \"1s\"/" config/akamai-purger.json if [ "$PKI_EMAIL_PASS" != "" ]; then sed -i -e "s/.*/$PKI_EMAIL_PASS/" secrets/smtp_password diff --git a/gui/main.go b/gui/main.go index c5be64f..bc4487c 100644 --- a/gui/main.go +++ b/gui/main.go @@ -3,6 +3,7 @@ package main import ( "bufio" "bytes" + "context" "crypto/aes" "crypto/cipher" "crypto/rand" @@ -11,7 +12,9 @@ import ( "errors" "fmt" "github.com/biz/templates" + "github.com/dustin/go-humanize" _ "github.com/go-sql-driver/mysql" + "github.com/google/go-github/github" "github.com/gorilla/mux" "github.com/gorilla/securecookie" "github.com/gorilla/sessions" @@ -39,20 +42,23 @@ import ( ) const ( - writeWait = 10 * time.Second - pongWait = 60 * time.Second - pingPeriod = (pongWait * 9) / 10 + writeWait = 10 * time.Second + pongWait = 60 * time.Second + pingPeriod = (pongWait * 9) / 10 + updateInterval = 24 * time.Hour ) var ( - appSession *sessions.Session - restartSecret string - sessionStore *sessions.CookieStore - tmpls *templates.Templates - version string - dbConn string - dbType string - isDev bool + appSession *sessions.Session + restartSecret string + sessionStore *sessions.CookieStore + tmpls *templates.Templates + version string + dbConn string + dbType string + isDev bool + updateAvailable bool + updateChecked time.Time upgrader = websocket.Upgrader{ ReadBufferSize: 1024, @@ -232,6 +238,43 @@ func errorHandler(w http.ResponseWriter, r *http.Request, err error, status int) } } +func checkUpdates(forced bool) ([]string, []string) { + var versions []string + var descriptions []string + + if forced || updateChecked.Add(updateInterval).Before(time.Now()) { + latest := "" + newer := true + + client := github.NewClient(nil) + + if releases, _, err := client.Repositories.ListReleases(context.Background(), "hakwerk", "labca", nil); err == nil { + for i := 0; i < len(releases); i++ { + release := releases[i] + if !*release.Draft { + if !*release.Prerelease || isDev { + if latest == "" { + latest = *release.Name + } + if *release.Name == version { + newer = false + } + if newer { + versions = append(versions, *release.Name) + descriptions = append(descriptions, *release.Body) + } + } + } + } + + updateChecked = time.Now() + updateAvailable = (len(releases) > 0) && (latest != version) + } + } + + return versions, descriptions +} + func rootHandler(w http.ResponseWriter, r *http.Request) { if !viper.GetBool("config.complete") { http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) @@ -240,6 +283,11 @@ func rootHandler(w http.ResponseWriter, r *http.Request) { dashboardData, err := CollectDashboardData(w, r) if err == nil { + checkUpdates(false) + dashboardData["UpdateAvailable"] = updateAvailable + dashboardData["UpdateChecked"] = strings.Replace(updateChecked.Format("02-Jan-2006 15:04:05 MST"), "+0000", "GMT", -1) + dashboardData["UpdateCheckedRel"] = humanize.RelTime(updateChecked, time.Now(), "", "") + render(w, r, "dashboard", dashboardData) } } @@ -798,6 +846,27 @@ func (res *Result) ManageComponents(w http.ResponseWriter, r *http.Request, acti } } +func _checkUpdatesHandler(w http.ResponseWriter, r *http.Request) { + res := struct { + Success bool + UpdateAvailable bool + UpdateChecked string + UpdateCheckedRel string + Versions []string + Descriptions []string + Errors map[string]string + }{Success: true, Errors: make(map[string]string)} + + res.Versions, res.Descriptions = checkUpdates(true) + res.UpdateAvailable = updateAvailable + res.UpdateChecked = updateChecked.Format("02-Jan-2006 15:04:05 MST") + res.UpdateChecked = strings.Replace(res.UpdateChecked, "+0000", "GMT", -1) + res.UpdateCheckedRel = humanize.RelTime(updateChecked, time.Now(), "", "") + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) +} + func _managePostDispatch(w http.ResponseWriter, r *http.Request, action string) bool { if action == "backup-restore" || action == "backup-delete" || action == "backup-now" { _backupHandler(w, r) @@ -829,6 +898,11 @@ func _managePostDispatch(w http.ResponseWriter, r *http.Request, action string) return true } + if action == "version-check" { + _checkUpdatesHandler(w, r) + return true + } + return false } @@ -858,6 +932,8 @@ func _managePost(w http.ResponseWriter, r *http.Request) { "update-config", "update-email", "send-email", + "version-check", + "version-update", } { if a == action { actionKnown = true @@ -878,7 +954,7 @@ func _managePost(w http.ResponseWriter, r *http.Request) { res.Message = "Command failed - see LabCA log for any details" } - if action != "server-restart" && action != "server-shutdown" { + if action != "server-restart" && action != "server-shutdown" && action != "version-update" { res.ManageComponents(w, r, action) } @@ -890,6 +966,11 @@ func _manageGet(w http.ResponseWriter, r *http.Request) { manageData := make(map[string]interface{}) manageData["RequestBase"] = r.Header.Get("X-Request-Base") + checkUpdates(false) + manageData["UpdateAvailable"] = updateAvailable + manageData["UpdateChecked"] = strings.Replace(updateChecked.Format("02-Jan-2006 15:04:05 MST"), "+0000", "GMT", -1) + manageData["UpdateCheckedRel"] = humanize.RelTime(updateChecked, time.Now(), "", "") + components := _parseComponents(getLog(w, r, "components")) for i := 0; i < len(components); i++ { if components[i].Name == "NGINX Webserver" { @@ -2292,6 +2373,8 @@ func init() { dbType = viper.GetString("db.type") version = viper.GetString("version") + + updateAvailable = false } func main() { diff --git a/gui/setup.sh b/gui/setup.sh index 71e299f..b63b928 100755 --- a/gui/setup.sh +++ b/gui/setup.sh @@ -9,6 +9,7 @@ if [ ! -e bin/labca ]; then go get github.com/biz/templates go get github.com/go-sql-driver/mysql go get github.com/dustin/go-humanize + go get github.com/google/go-github/github go get github.com/gorilla/mux go get github.com/gorilla/securecookie go get github.com/gorilla/sessions diff --git a/gui/templates/base.tmpl b/gui/templates/base.tmpl index faa69b6..2f2a306 100644 --- a/gui/templates/base.tmpl +++ b/gui/templates/base.tmpl @@ -29,7 +29,7 @@