Files
labca/gui/upgrades.go
2025-04-20 17:27:10 +02:00

195 lines
5.1 KiB
Go

package main
import (
"bufio"
"errors"
"fmt"
"io"
"io/fs"
"log"
"net"
"os"
"strings"
"time"
"github.com/spf13/viper"
)
func CheckUpgrades() {
v := viper.GetString("version")
if standaloneVersion == "" {
gitVersion := controlCommand("git-version", true)
if gitVersion != "" {
viper.Set("version", strings.TrimSpace(gitVersion))
_ = viper.WriteConfig()
}
} else if v != standaloneVersion {
viper.Set("version", standaloneVersion)
_ = viper.WriteConfig()
}
changed := CheckUpgrade_01_CeremonyHSM()
if changed {
time.Sleep(2 * time.Second)
log.Println("Applying updated configuration...")
controlCommand("apply", false)
time.Sleep(2 * time.Second)
log.Println("Updating CRL links if needed...")
controlCommand("check-crl", false)
time.Sleep(2 * time.Second)
log.Println("Restarting boulder containers...")
controlCommand("boulder-restart", false)
}
}
func readFileAsString(filename string) string {
read, err := os.ReadFile(filename)
if err != nil {
log.Printf("**** Could not read '%s': %s\n", filename, err.Error())
log.Println("**** ABORT MIGRATION ****")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
return string(read)
}
func controlCommand(command string, ignoreError bool) string {
conn, err := net.Dial("tcp", "control:3030")
if err != nil {
if ignoreError {
return ""
}
log.Println("**** Failed to connect to control container!")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
defer func() { _ = conn.Close() }()
_, _ = fmt.Fprint(conn, command+"\n")
reader := bufio.NewReader(conn)
message, err := io.ReadAll(reader)
if err != nil {
log.Printf("**** Failed to read response from control container: %s\n", err.Error())
time.Sleep(1 * time.Minute)
os.Exit(1)
}
if len(message) >= 4 {
tail := message[len(message)-4:]
if strings.Compare(string(tail), "\nok\n") == 0 {
msg := message[0 : len(message)-4]
log.Printf("**** Message from control server: '%s'", msg)
}
}
return string(message)
}
func copyFile(src, dst string) error {
sourceFile, err := os.Open(src)
if err != nil {
return err
}
defer func() { _ = sourceFile.Close() }()
destinationFile, err := os.Create(dst)
if err != nil {
return err
}
defer func() { _ = destinationFile.Close() }()
_, err = io.Copy(destinationFile, sourceFile)
if err != nil {
return err
}
err = destinationFile.Sync()
if err != nil {
return err
}
return nil
}
// Check if we should upgrade to using the Ceremony tool and store keys on SoftHSM (January 2025).
func CheckUpgrade_01_CeremonyHSM() bool {
baseDir := "/opt/labca/data/"
prevRootCert := baseDir + "root-ca.pem"
if _, err := os.Stat(prevRootCert); errors.Is(err, fs.ErrNotExist) {
baseDir = "/go/src/labca/data/"
prevRootCert = baseDir + "root-ca.pem"
if _, err := os.Stat(prevRootCert); errors.Is(err, fs.ErrNotExist) {
return false
}
}
log.Println("**** BEGIN MIGRATION: upgrade01 ****")
rootCertFile := fmt.Sprintf("%sroot-01-cert.pem", CERT_FILES_PATH)
if _, err := os.Stat(rootCertFile); !errors.Is(err, fs.ErrNotExist) {
log.Printf("**** File %s already exists!\n", rootCertFile)
log.Println("**** ABORT MIGRATION ****")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
prevRootKey := baseDir + "root-ca.key"
if _, err := os.Stat(prevRootKey); errors.Is(err, fs.ErrNotExist) {
log.Println("**** Root key file not present on the system: cannot upgrade automatically!")
log.Println("**** Please do a fresh install of LabCA and import / upload the root certificate and key.")
log.Println("**** ABORT MIGRATION ****")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
// Migrate root certificate and key
ci := &CertificateInfo{IsRoot: true}
ci.Initialize()
ci.IsRoot = true
ci.CreateType = "upload"
ci.Certificate = readFileAsString(prevRootCert)
ci.Key = readFileAsString(prevRootKey)
prevRootCRL := baseDir + "root-ca.crl"
if _, err := os.Stat(prevRootCRL); !errors.Is(err, fs.ErrNotExist) {
ci.CRL = readFileAsString(prevRootCRL)
_ = copyFile(prevRootCRL, strings.ReplaceAll(rootCertFile, "-cert.", "-crl."))
}
if err := ci.Create("root-01", false); err != nil {
log.Printf("**** Could not convert previous root certificate and key: %s\n", err.Error())
log.Println("**** ABORT MIGRATION ****")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
// Migrate issuer certificate and key
ci = &CertificateInfo{IsRoot: false}
ci.Initialize()
ci.IsRoot = false
ci.CreateType = "upload"
prevIssuerCert := baseDir + "issuer/ca-int.pem"
ci.Certificate = readFileAsString(prevIssuerCert)
prevIssuerKey := baseDir + "issuer/ca-int.key"
ci.Key = readFileAsString(prevIssuerKey)
ci.CRL = ""
if err := ci.Create("issuer-01", false); err != nil {
log.Printf("**** Could not convert previous issuer certificate and key: %s\n", err.Error())
log.Println("**** ABORT MIGRATION ****")
time.Sleep(1 * time.Minute)
os.Exit(1)
}
_ = os.Rename(prevRootCert, prevRootCert+"_backup")
_ = os.Rename(prevRootKey, prevRootKey+"_backup")
_ = os.Rename(prevRootCRL, prevRootCRL+"_backup")
_ = os.Rename(prevIssuerCert, prevIssuerCert+"_backup")
_ = os.Rename(prevIssuerKey, prevIssuerKey+"_backup")
log.Println("**** END MIGRATION ****")
return true
}