Update LabCA GUI for several recent boulder changes

This commit is contained in:
Arjan H
2024-08-27 07:32:29 +02:00
parent 18b53030a1
commit ab35a620f7
4 changed files with 72 additions and 87 deletions

View File

@@ -33,8 +33,7 @@ type IssuerLoc struct {
// From boulder: issuance/issuer.go
type IssuerConfig struct {
UseForRSALeaves bool `json:"useForRSALeaves"`
UseForECDSALeaves bool `json:"useForECDSALeaves"`
Active bool `json:"active"`
IssuerURL string `validate:"required,url" json:"issuerURL,omitempty"`
OCSPURL string `validate:"required,url" json:"ocspURL,omitempty"`
@@ -56,14 +55,13 @@ type CAConfig struct {
// CertDetails contains info about each certificate for use in the GUI
type CertDetails struct {
CertFile string
BaseName string
Subject string
IsRoot bool
UseForRSA bool
UseForECDSA bool
NotAfter string
Details string
CertFile string
BaseName string
Subject string
IsRoot bool
ActiveIssuer bool
NotAfter string
Details string
}
type CertChain struct {
@@ -139,8 +137,7 @@ func enhanceChains(chains []CertChain) []CertChain {
for k := 0; k < len(chains); k++ {
for n := 0; n < len(chains[k].IssuerCerts); n++ {
if chains[k].IssuerCerts[n].CertFile == rawChains[i].Location.CertFile {
chains[k].IssuerCerts[n].UseForRSA = rawChains[i].UseForRSALeaves
chains[k].IssuerCerts[n].UseForECDSA = rawChains[i].UseForECDSALeaves
chains[k].IssuerCerts[n].ActiveIssuer = rawChains[i].Active
certFile := locateFile(rawChains[i].Location.CertFile)
if d, err := getCertFileDetails(certFile); err == nil {
chains[k].IssuerCerts[n].Details = d
@@ -235,7 +232,7 @@ func getChains() []CertChain {
return chains
}
func setUseForLeavesFile(filename, forRSA, forECDSA string) error {
func setUseForLeavesFile(filename, active string) error {
caConf, err := os.Open(filename)
if err != nil {
fmt.Println(err)
@@ -251,30 +248,20 @@ func setUseForLeavesFile(filename, forRSA, forECDSA string) error {
}
// Make sure that the named certificate(s) exist
foundRSA := false
foundECDSA := false
foundActive := false
for i := 0; i < len(result.CA.Issuance.Issuers); i++ {
if strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, forRSA) {
foundRSA = true
}
if strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, forECDSA) {
foundECDSA = true
if strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, active) {
foundActive = true
}
}
if !foundRSA {
return errors.New("certificate '" + forRSA + "' not found in ca file")
}
if !foundECDSA {
return errors.New("certificate '" + forECDSA + "' not found in ca file")
if !foundActive {
return errors.New("certificate '" + active + "' not found in ca file")
}
// Now set the flags for the named certificate(s)
for i := 0; i < len(result.CA.Issuance.Issuers); i++ {
if forRSA != "" {
result.CA.Issuance.Issuers[i].UseForRSALeaves = strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, forRSA)
}
if forECDSA != "" {
result.CA.Issuance.Issuers[i].UseForECDSALeaves = strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, forECDSA)
if active != "" {
result.CA.Issuance.Issuers[i].Active = strings.Contains(result.CA.Issuance.Issuers[i].Location.CertFile, active)
}
}
@@ -302,25 +289,22 @@ func setUseForLeavesFile(filename, forRSA, forECDSA string) error {
return nil
}
func setUseForLeaves(forRSA, forECDSA string) error {
func setUseForLeaves(active string) error {
if err := exec.Command("cp", "-f", caConfFile, caConfFile+"_BAK").Run(); err != nil {
return errors.New("could not create ca backup file: " + err.Error())
}
if err := setUseForLeavesFile(caConfFile, forRSA, forECDSA); err != nil {
if err := setUseForLeavesFile(caConfFile, active); err != nil {
exec.Command("mv", caConfFile+"_BAK", caConfFile).Run()
return err
}
exec.Command("rm", caConfFile+"_BAK").Run()
if forRSA != "" {
viper.Set("certs.issuerRSA", forRSA)
if active != "" {
viper.Set("certs.activeIssuer", active)
}
if forECDSA != "" {
viper.Set("certs.issuerECDSA", forECDSA)
}
if forRSA != "" || forECDSA != "" {
if active != "" {
viper.WriteConfig()
}

View File

@@ -194,6 +194,7 @@ func _parseComponents(data string) []Component {
if len(parts) < 6 {
components = append(components, Component{Name: "Boulder (ACME)"})
components = append(components, Component{Name: "Consul (Boulder)"})
components = append(components, Component{Name: "pkilint (Boulder)"})
components = append(components, Component{Name: "LabCA Application"})
components = append(components, Component{Name: "LabCA Controller"})
components = append(components, Component{Name: "MySQL Database"})
@@ -261,8 +262,19 @@ func _parseComponents(data string) []Component {
consulClass = ""
}
pkilint, err := time.Parse(time.RFC3339Nano, parts[6])
pkilintReal := ""
pkilintNice := "stopped"
pkilintClass := "error"
if err == nil {
pkilintReal = pkilint.Format("02-Jan-2006 15:04:05 MST")
pkilintNice = humanize.RelTime(pkilint, time.Now(), "", "")
pkilintClass = ""
}
components = append(components, Component{Name: "Boulder (ACME)", Timestamp: boulderReal, TimestampRel: boulderNice, Class: boulderClass})
components = append(components, Component{Name: "Consul (Boulder)", Timestamp: consulReal, TimestampRel: consulNice, Class: consulClass})
components = append(components, Component{Name: "pkilint (Boulder)", Timestamp: pkilintReal, TimestampRel: pkilintNice, Class: pkilintClass})
components = append(components, Component{Name: "LabCA Application", Timestamp: labcaReal, TimestampRel: labcaNice, Class: labcaClass})
components = append(components, Component{Name: "LabCA Controller", Timestamp: svcReal, TimestampRel: svcNice, Class: svcClass})
components = append(components, Component{Name: "MySQL Database", Timestamp: mysqlReal, TimestampRel: mysqlNice, Class: mysqlClass})
@@ -472,6 +484,9 @@ func parseDockerStats(data string) []AjaxStat {
if strings.Contains(docker.Name, "-bconsul-") {
stat.Name = "Consul (Boulder)"
}
if strings.Contains(docker.Name, "-bpkilint-") {
stat.Name = "pkilint (Boulder)"
}
if strings.Contains(docker.Name, "labca-gui-") {
stat.Name = "LabCA Application"
}

View File

@@ -1078,6 +1078,7 @@ func (res *Result) ManageComponents(w http.ResponseWriter, r *http.Request, acti
(components[i].Name == "Boulder (ACME)" && (action == "boulder-start" || action == "boulder-stop" || action == "boulder-restart")) ||
(components[i].Name == "LabCA Application" && action == "labca-restart") ||
(components[i].Name == "Consul (Boulder)" && action == "consul-restart") ||
(components[i].Name == "pkilint (Boulder)" && action == "pkilint-restart") ||
(components[i].Name == "MySQL Database" && action == "mysql-restart") {
res.Timestamp = components[i].Timestamp
res.TimestampRel = components[i].TimestampRel
@@ -1203,7 +1204,7 @@ func updateLeaveIssuersHandler(w http.ResponseWriter, r *http.Request) {
Error string
}{Success: true}
if err := setUseForLeaves(r.Form.Get("rsa"), r.Form.Get("ecdsa")); err != nil {
if err := setUseForLeaves(r.Form.Get("active")); err != nil {
res.Success = false
res.Error = err.Error()
} else {
@@ -1359,6 +1360,7 @@ func _managePost(w http.ResponseWriter, r *http.Request) {
"cert-export",
"mysql-restart",
"consul-restart",
"pkilint-restart",
"nginx-reload",
"nginx-restart",
"svc-restart",
@@ -1552,6 +1554,18 @@ func _manageGet(w http.ResponseWriter, r *http.Request) {
components[i].Buttons = append(components[i].Buttons, btn)
}
if components[i].Name == "pkilint (Boulder)" {
components[i].LogURL = ""
components[i].LogTitle = ""
btn := make(map[string]interface{})
btn["Class"] = "btn-warning"
btn["Id"] = "pkilint-restart"
btn["Title"] = "Restart the internal pkilint helper"
btn["Label"] = "Restart"
components[i].Buttons = append(components[i].Buttons, btn)
}
if components[i].Name == "MySQL Database" {
components[i].LogURL = ""
components[i].LogTitle = ""
@@ -3549,4 +3563,4 @@ func main() {
} else {
log.Fatal(srv.ListenAndServe())
}
}
}

View File

@@ -165,8 +165,7 @@
<tr>
<th>Subject</th>
<th></th>
<th>Issue RSA</th>
<th>Issue ECDSA</th>
<th>Active</th>
<th></th>
</tr>
{{ range $item := .CertificateChains }}
@@ -179,7 +178,6 @@
<a href="/certs/{{ $item.RootCert.BaseName }}.pem" title="Download this certificate">download</a>
</td>
<td class="vmiddle"></td>
<td class="vmiddle"></td>
<td class="vmiddle">
<button class="btn btn-outline btn-reg btn-success export-cert-key" type="button" title="Export this certificate and key" data-name="{{ $item.RootCert.BaseName }}" data-subject="{{ $item.RootCert.Subject }}">Export</button>
<button class="btn btn-outline btn-reg btn-warning renew-cert" type="button" title="Generate new version with extended lifetime" data-name="{{ $item.RootCert.BaseName }}" data-rootname="{{ $item.RootCert.BaseName }}" data-subject="{{ $item.RootCert.Subject }}" data-rootsubject="{{ $item.RootCert.Subject }}" data-notbefore="{{ $item.RootCert.NotAfter }}" data-isroot="true">Renew</button>
@@ -198,8 +196,7 @@
<td class="vmiddle">
<a href="/certs/{{ $subitem.BaseName }}.pem" title="Download this certificate">download</a>
</td>
<td class="vmiddle center"><input type="radio" id="rsa-{{ $subitem.BaseName }}" name="issue-rsa" value="{{ $subitem.BaseName }}" title="Use this certificate for issueing RSA leave certificates" {{ if $subitem.UseForRSA }}data-orig="true" checked{{ end }}/></td>
<td class="vmiddle center"><input type="radio" id="ecdsa-{{ $subitem.BaseName }}" name="issue-ecdsa" value="{{ $subitem.BaseName }}" title="Use this certificate for issueing ECDSA leave certificates" {{ if $subitem.UseForECDSA }}data-orig="true" checked{{ end }}/></td>
<td class="vmiddle center"><input type="radio" id="active-{{ $subitem.BaseName }}" name="issue-active" value="{{ $subitem.BaseName }}" title="Use this certificate for issueing leave certificates" {{ if $subitem.ActiveIssuer }}data-orig="true" checked{{ end }}/></td>
<td class="vmiddle">
<button class="btn btn-outline btn-reg btn-success export-cert-key" type="button" title="Export this certificate and key" data-name="{{ $subitem.BaseName }}" data-subject="{{ $subitem.Subject }}">Export</button>
<button class="btn btn-outline btn-reg btn-warning renew-cert" type="button" title="Generate new version with extended lifetime" data-name="{{ $subitem.BaseName }}" data-rootname="{{ $item.RootCert.BaseName }}" data-subject="{{ $subitem.Subject }}" data-rootsubject="{{ $item.RootCert.Subject }}" data-notbefore="{{ $subitem.NotAfter }}" data-notafter="{{ $item.RootCert.NotAfter }}" data-isroot="false">Renew</button>
@@ -527,10 +524,8 @@
<h4>Are you sure?</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span>&times;</span></button>
<form enctype="multipart/form-data" id="modal-leaves-form" method="POST">
<input type="hidden" id="new-rsa-issuer" name="new-rsa-issuer">
<input type="hidden" id="new-ecdsa-issuer" name="new-ecdsa-issuer">
<span id="want-new-rsa-issuer" class="hidden">&#x2022; Change the RSA issuer certificate<br/></span>
<span id="want-new-ecdsa-issuer" class="hidden">&#x2022; Change the ECDSA issuer certificate<br/></span>
<input type="hidden" id="new-active-issuer" name="new-active-issuer">
<span id="want-new-active-issuer" class="hidden">&#x2022; Change the active issuer certificate<br/></span>
<span class="error" id="modal-leaves-error" style="display: none;"></span><br/>
<input class="btn btn-default btn-reg btn-warning btn-6em" value="Change" id="modal-leaves-done"/>
<button type="button" class="btn btn-default" data-dismiss="modal" id="cancel-leaves">Cancel</button>
@@ -614,49 +609,29 @@
{{ end }}
function leave_issuer_changed() {
iss_rsa = "";
iss_ecdsa = "";
iss_rsa_changed = false;
iss_ecdsa_changed = false;
iss_active = "";
iss_active_changed = false;
$('input[type=radio][name=issue-rsa]').each(function() {
$('input[type=radio][name=issue-active]').each(function() {
if ($(this).prop('checked') && !$(this).data('orig')) {
iss_rsa = $(this).val();
iss_rsa_changed = true;
}
});
$('input[type=radio][name=issue-ecdsa]').each(function() {
if ($(this).prop('checked') && !$(this).data('orig')) {
iss_ecdsa = $(this).val();
iss_ecdsa_changed = true;
iss_active = $(this).val();
iss_active_changed = true;
}
});
if (iss_rsa_changed || iss_ecdsa_changed) {
$("#new-rsa-issuer").val(iss_rsa);
if (iss_rsa_changed) {
$("#want-new-rsa-issuer").removeClass("hidden").show();
} else {
$("#want-new-rsa-issuer").hide();
}
$("#new-ecdsa-issuer").val(iss_ecdsa);
if (iss_ecdsa_changed) {
$("#want-new-ecdsa-issuer").removeClass("hidden").show();
} else {
$("#want-new-ecdsa-issuer").hide();
}
if (iss_active_changed) {
$("#new-active-issuer").val(iss_active);
$("#want-new-active-issuer").removeClass("hidden").show();
$('#modal-leaves').modal('show');
} else {
$("#want-new-active-issuer").hide();
}
return false;
}
$('input[type=radio][name=issue-rsa]').change(function() {
return leave_issuer_changed();
});
$('input[type=radio][name=issue-ecdsa]').change(function() {
$('input[type=radio][name=issue-active]').change(function() {
return leave_issuer_changed();
});
@@ -1313,7 +1288,7 @@
return true;
} else if ($(evt.target).attr("id") == "modal-leaves-done") {
if ($("#want-new-rsa-issuer").hasClass("hidden") && $("#want-new-ecdsa-issuer").hasClass("hidden")) {
if ($("#want-new-active-issuer").hasClass("hidden")) {
$('#modal-spinner').modal('hide');
$('#modal-leaves').modal('hide');
} else {
@@ -1328,10 +1303,7 @@
try {
res = JSON.parse(req.response);
if (res.Success) {
$('input[type=radio][name=issue-rsa]').each(function() {
$(this).data('orig', $(this).prop('checked'))
});
$('input[type=radio][name=issue-ecdsa]').each(function() {
$('input[type=radio][name=issue-active]').each(function() {
$(this).data('orig', $(this).prop('checked'))
});
$('#modal-leaves').modal('hide');
@@ -1368,7 +1340,7 @@
$("#modal-leaves-error").removeClass("hidden").show().text("Error communicating with backend server");
};
req.send("action=update-leave-issuers" + "&rsa=" + $("#new-rsa-issuer").val() + "&ecdsa=" + $("#new-ecdsa-issuer").val());
req.send("action=update-leave-issuers" + "&active=" + $("#new-active-issuer").val());
}
return false;
@@ -1669,4 +1641,4 @@
pwduxHandlers('#password-strength', '#new-password', ['#username', '#accemail']);
});
</script>
{{ end }}
{{ end }}