diff --git a/README.md b/README.md index 9821da6..0b4fb7b 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,11 @@ echo "LABCA_IMAGE_VERSION=v25.03" > labca.env ## Usage -Once LabCA has been setup you should go through the admin pages and e.g. configure the email details for outgoing notifications. Now your instance is ready to provide HTTPS certificates for your internal applications. +Once LabCA has been setup, your instance is ready to provide HTTPS certificates for your internal applications. ### Admin -The admin section is only accessible to the user account created at the start of the setup. The [dashboard](https://user-images.githubusercontent.com/44847421/48658726-ebd4c400-ea46-11e8-8cb1-43584dbc3719.jpg) gives an overview of the current status of your LabCA instance. Via the menu you can navigate to the details of your ACME objects such as the certificates, to several system logfiles and to the various management tasks such as backup/restore, email settings and changing your password. +The admin section is only accessible to the user account created at the start of the setup. The [dashboard](https://user-images.githubusercontent.com/44847421/48658726-ebd4c400-ea46-11e8-8cb1-43584dbc3719.jpg) gives an overview of the current status of your LabCA instance. Via the menu you can navigate to the details of your ACME objects such as the certificates, to several system logfiles and to the various management tasks such as backup/restore and changing your password. These screenshots give a preview of the admin section: diff --git a/build/Dockerfile-control b/build/Dockerfile-control index 82031b4..e6bcf8c 100644 --- a/build/Dockerfile-control +++ b/build/Dockerfile-control @@ -61,7 +61,6 @@ COPY tmp/checkrenew /opt/labca/ COPY tmp/commander /opt/labca/ COPY tmp/control.sh /opt/labca/ COPY tmp/cron_d /opt/labca/ -COPY tmp/mailer /opt/labca/ COPY tmp/renew /opt/labca/ COPY tmp/restore /opt/labca/ COPY tmp/utils.sh /opt/labca/ diff --git a/build/build.sh b/build/build.sh index 130bac2..d898926 100755 --- a/build/build.sh +++ b/build/build.sh @@ -59,7 +59,6 @@ cp -rp $cloneDir/checkrenew $TMP_DIR/ cp -rp $cloneDir/commander $TMP_DIR/ cp -rp $cloneDir/control_do.sh $TMP_DIR/control.sh cp -rp $cloneDir/cron_d $TMP_DIR/ -cp -rp $cloneDir/mailer $TMP_DIR/ cp -rp $cloneDir/renew $TMP_DIR/ cp -rp $cloneDir/restore $TMP_DIR/ cp -rp $cloneDir/utils.sh $TMP_DIR/ diff --git a/commander b/commander index ae475aa..8667791 100755 --- a/commander +++ b/commander @@ -165,9 +165,6 @@ case $txt in # NOTE: admin is one of the few commands that is NOT a subcommand of boulder! docker compose exec boulder bin/admin -config labca/config/admin.json revoke-cert -serial $serial -reason $reason -dry-run=false 2>&1 ;; -"test-email") - read recipient - ;; "boulder-start") cd /opt/boulder COMPOSE_HTTP_TIMEOUT=120 docker compose up -d bmysql bconsul bpkimetal bredis diff --git a/gui/apply-boulder b/gui/apply-boulder index 01b7bf0..2ca1491 100755 --- a/gui/apply-boulder +++ b/gui/apply-boulder @@ -30,36 +30,6 @@ if [ "$extended_timeout" != "" ]; then else PKI_EXTENDED_TIMEOUT=0 fi -enabled=$(grep "email\": {" $dataDir/config.json -A1 | grep enable | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') -if [ "$enabled" == "true," ]; then - PKI_EMAIL_SERVER=$(grep server $dataDir/config.json | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - PKI_EMAIL_PORT=$(grep port $dataDir/config.json | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - PKI_EMAIL_USER=$(grep user $dataDir/config.json | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - PKI_EMAIL_PASS=$(grep pass $dataDir/config.json | grep -v password | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - pwd="" - if [ -e $baseDir/bin/labca-gui ]; then - pwd=$([ -e ] && $baseDir/bin/labca-gui -config $dataDir/config.json -d $PKI_EMAIL_PASS || echo "") - elif [ -e $baseDir/bin/labca-gui_prev ]; then - pwd=$([ -e ] && $baseDir/bin/labca-gui_prev -config $dataDir/config.json -d $PKI_EMAIL_PASS || echo "") - fi - PKI_EMAIL_PASS=$pwd - PKI_EMAIL_FROM=$(grep from $dataDir/config.json | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - PKI_EMAIL_TRUST=$(grep trust_root $dataDir/config.json | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g') - if [ "$PKI_EMAIL_TRUST" == "private" ]; then - PKI_EMAIL_TRUST="labca/certs/webpki/root-01-cert.pem" - elif [ "$PKI_EMAIL_TRUST" == "skip" ]; then - PKI_EMAIL_TRUST="InsecureSkipVerify" - else - PKI_EMAIL_TRUST="" - fi -else - PKI_EMAIL_SERVER="localhost" - PKI_EMAIL_PORT="9380" - PKI_EMAIL_USER="cert-manager@example.com" - PKI_EMAIL_PASS="password" - PKI_EMAIL_FROM="Expiry bot " - PKI_EMAIL_TRUST="labca/certs/ipki/minica.pem" -fi perl -i -p0e "s/(\"dnsStaticResolvers\": \[\n).*?(\s+\],)/\1\t\t\t\"$PKI_DNS\"\2/igs" config/remoteva-a.json @@ -207,12 +177,6 @@ else fi sed -i -e "s/\"timeout\": \"1s\"/\"timeout\": \"5s\"/" config/health-checker.json -sed -i -e "s/\"server\": \".*\"/\"server\": \"$PKI_EMAIL_SERVER\"/" config/bad-key-revoker.json -sed -i -e "s/\"port\": \".*\"/\"port\": \"$PKI_EMAIL_PORT\"/" config/bad-key-revoker.json -sed -i -e "s/\"username\": \".*\"/\"username\": \"$PKI_EMAIL_USER\"/" config/bad-key-revoker.json -sed -i -e "s/\"from\": \".*\"/\"from\": \"$PKI_EMAIL_FROM\"/" config/bad-key-revoker.json -sed -i -e "s|\"SMTPTrustedRootFile\": \".*\"|\"SMTPTrustedRootFile\": \"$PKI_EMAIL_TRUST\"|" config/bad-key-revoker.json - sed -i -e "s/\"purgeInterval\": \".*\"/\"purgeInterval\": \"1s\"/" config/akamai-purger.json for fl in $(grep -Rl maxOpenConns config/); do @@ -224,10 +188,6 @@ for fl in $(grep -Rl maxOpenConns config/); do fi done -if [ "$PKI_EMAIL_PASS" != "" ]; then - sed -i -e "s/.*/$PKI_EMAIL_PASS/" secrets/smtp_password -fi - rm -f test-ca.key rm -f test-ca.key.der rm -f test-ca.pem diff --git a/gui/main.go b/gui/main.go index 75f449a..60a16e7 100644 --- a/gui/main.go +++ b/gui/main.go @@ -813,167 +813,6 @@ func _crlIntervalUpdateHandler(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(res) } -// EmailConfig stores configuration used for sending out emails -type EmailConfig struct { - DoEmail bool - Server string - Port string - EmailUser string - EmailPwd []byte - From string - TrustRoot string - Errors map[string]string -} - -// Validate that the email config is valid and complete -func (cfg *EmailConfig) Validate() bool { - cfg.Errors = make(map[string]string) - - result, err := _encrypt(cfg.EmailPwd) - if err == nil { - cfg.EmailPwd = []byte(result) - } else { - cfg.Errors["EmailPwd"] = "Could not encrypt this password: " + err.Error() - } - - if !cfg.DoEmail { - return len(cfg.Errors) == 0 - } - - if strings.TrimSpace(cfg.Server) == "" { - cfg.Errors["Server"] = "Please enter the email server address" - } - - if strings.TrimSpace(cfg.Port) == "" { - cfg.Errors["Port"] = "Please enter the email server port number" - } - - p, err := strconv.Atoi(cfg.Port) - if err != nil { - cfg.Errors["Port"] = "Port number must be numeric" - } else if p <= 0 { - cfg.Errors["Port"] = "Port number must be positive" - } else if p > 65535 { - cfg.Errors["Port"] = "Port number too large" - } - - if strings.TrimSpace(cfg.EmailUser) == "" { - cfg.Errors["EmailUser"] = "Please enter the username for authorization to the email server" - } - - res, err := _decrypt(string(cfg.EmailPwd)) - if err != nil { - cfg.Errors["EmailPwd"] = "Could not decrypt this password: " + err.Error() - } - if strings.TrimSpace(string(res)) == "" { - cfg.Errors["EmailPwd"] = "Please enter the password for authorization to the email server" - } - - if strings.TrimSpace(cfg.From) == "" { - cfg.Errors["From"] = "Please enter the from email address" - } - - if strings.TrimSpace(cfg.TrustRoot) == "" { - cfg.Errors["From"] = "Please select what root CA to trust for validating the email server certificate" - } - - return len(cfg.Errors) == 0 -} - -func _emailUpdateHandler(w http.ResponseWriter, r *http.Request) { - cfg := &EmailConfig{ - DoEmail: (r.Form.Get("do_email") == "true"), - Server: r.Form.Get("server"), - Port: r.Form.Get("port"), - EmailUser: r.Form.Get("email_user"), - EmailPwd: []byte(r.Form.Get("email_pwd")), - From: r.Form.Get("from"), - TrustRoot: r.Form.Get("trust_root"), - } - - res := makeErrorsResponse(true) - - if cfg.Validate() { - delta := false - - if cfg.DoEmail != viper.GetBool("labca.email.enable") { - delta = true - viper.Set("labca.email.enable", cfg.DoEmail) - } - - if cfg.Server != viper.GetString("labca.email.server") { - delta = true - viper.Set("labca.email.server", cfg.Server) - } - - if cfg.Port != viper.GetString("labca.email.port") { - delta = true - viper.Set("labca.email.port", cfg.Port) - } - - if cfg.EmailUser != viper.GetString("labca.email.user") { - delta = true - viper.Set("labca.email.user", cfg.EmailUser) - } - - res1, err1 := _decrypt(string(cfg.EmailPwd)) - if err1 != nil && cfg.DoEmail { - log.Println("WARNING: could not decrypt given password: " + err1.Error()) - } - res2, err2 := _decrypt(viper.GetString("labca.email.pass")) - if err2 != nil && cfg.DoEmail && viper.GetString("labca.email.pass") != "" { - log.Println("WARNING: could not decrypt stored password: " + err2.Error()) - } - if string(res1) != string(res2) { - delta = true - viper.Set("labca.email.pass", string(cfg.EmailPwd)) - } - - if cfg.From != viper.GetString("labca.email.from") { - delta = true - viper.Set("labca.email.from", cfg.From) - } - - if cfg.TrustRoot != viper.GetString("labca.email.trust_root") { - delta = true - viper.Set("labca.email.trust_root", cfg.TrustRoot) - } - - if delta { - _ = viper.WriteConfig() - - err := _applyConfig() - if err != nil { - res.Success = false - res.Errors = cfg.Errors - res.Errors["EmailUpdate"] = "Config apply error: '" + err.Error() + "'" - } - } else { - res.Success = false - res.Errors = cfg.Errors - res.Errors["EmailUpdate"] = "Nothing changed!" - } - - } else { - res.Success = false - res.Errors = cfg.Errors - } - - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(res) -} - -func _emailSendHandler(w http.ResponseWriter, r *http.Request) { - res := makeErrorsResponse(true) - - recipient := viper.GetString("user.email") - if _hostCommand(w, r, "test-email", recipient) { - // Only on success, as when this returns false for this case the response has already been sent! - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(res) - } -} - func _exportHandler(w http.ResponseWriter, r *http.Request) { certname := r.Form.Get("certname") certFile := fmt.Sprintf("%s%s.pem", CERT_FILES_PATH, certname) @@ -1281,16 +1120,6 @@ func _managePostDispatch(w http.ResponseWriter, r *http.Request, action string) return true } - if action == "update-email" { - _emailUpdateHandler(w, r) - return true - } - - if action == "send-email" { - _emailSendHandler(w, r) - return true - } - if action == "version-check" { _checkUpdatesHandler(w, r) return true @@ -1369,8 +1198,6 @@ func _managePost(w http.ResponseWriter, r *http.Request) { "update-backend", "update-config", "update-crl-interval", - "update-email", - "send-email", "version-check", "version-update", "upload-root-crl", @@ -1620,23 +1447,6 @@ func _manageGet(w http.ResponseWriter, r *http.Request) { manageData["WhitelistDomains"] = viper.GetString("labca.whitelist") } manageData["ExtendedTimeout"] = viper.GetBool("labca.extended_timeout") - - manageData["DoEmail"] = viper.GetBool("labca.email.enable") - manageData["Server"] = viper.GetString("labca.email.server") - manageData["Port"] = viper.GetInt("labca.email.port") - manageData["EmailUser"] = viper.GetString("labca.email.user") - manageData["EmailPwd"] = "" - if viper.Get("labca.email.pass") != nil { - pwd := viper.GetString("labca.email.pass") - result, err := _decrypt(pwd) - if err == nil { - manageData["EmailPwd"] = string(result) - } else { - log.Printf("WARNING: could not decrypt email password: %s!\n", err.Error()) - } - } - manageData["From"] = viper.GetString("labca.email.from") - manageData["TrustRoot"] = viper.GetString("labca.email.trust_root") } manageData["Name"] = viper.GetString("user.name") @@ -2307,18 +2117,6 @@ func _hostCommand(w http.ResponseWriter, r *http.Request, command string, params } log.Printf("ERROR: Message from server: '%s'", message) - if command == "test-email" { - // Want special error handling for this case - res := makeErrorsResponse(false) - if strings.Contains(string(message), "certificate signed by unknown authority") { - res.Errors["EmailSend"] = "Error: SMTP server certificate signed by unknown authority" - } else { - res.Errors["EmailSend"] = "Failed to send email - see logs" - } - w.Header().Set("Content-Type", "application/json") - _ = json.NewEncoder(w).Encode(res) - return false - } errorHandler(w, r, errors.New(string(message)), http.StatusInternalServerError) return false } diff --git a/gui/templates/views/manage.tmpl b/gui/templates/views/manage.tmpl index 37fa21f..11a068a 100644 --- a/gui/templates/views/manage.tmpl +++ b/gui/templates/views/manage.tmpl @@ -23,9 +23,6 @@
  • Config
  • -
  • - Email -
  • {{ end }}
  • Account @@ -318,54 +315,6 @@ -
    -
    -
    -
    - - Send emails about upcoming certificate expiration -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    - - - -
    -
    -
    - Trust standard public root CAs
    - Trust the LabCA root CA
    - Do not check server certificate
    -
    -
    - - -
    -
    - - -
    -
    -
    {{ end }}
    @@ -665,9 +614,6 @@ $("#update-account-result").hide(); $(".config-error").hide(); $("#update-config-result").hide(); - $(".email-error").hide(); - $("#update-email-result").hide(); - $("#send-email-result").hide(); $("#root-crl-result").hide(); $("#issuer-crl-result").hide(); $("#crl-interval-result").hide(); @@ -1030,83 +976,6 @@ $("#update-config-result").removeClass("hidden").removeClass("success").show().html(err + "
    ").addClass("error"); }); - } else if ( $(evt.target).attr("id") == "update-email") { - form = $(evt.target).parent().parent()[0] - if (!form.checkValidity()) { - $('#modal-spinner').modal('hide'); - const tmpSubmit = document.createElement('button') - form.appendChild(tmpSubmit) - tmpSubmit.click() - form.removeChild(tmpSubmit) - } else { - - $.ajax(window.location.href, { - method: "POST", - data: { - action: $(evt.target).attr("id"), - do_email: $("#email-cb").prop("checked"), - server: $("#server").val(), - port: $("#port").val(), - email_user: $("#emailuser").val(), - email_pwd: $("#emailpwd").val(), - from: $("#from").val(), - trust_root: $("#rootfile-private").prop('checked') ? "private" : ($("#rootfile-skip").prop('checked') ? "skip" : "public"), - }, - }) - .done(function(data) { - $('#modal-spinner').modal('hide'); - - if (data.Success) { - var msg = "Successfully updated configuration.
    "; - $("#update-email-result").removeClass("hidden").removeClass("error").show().html(msg).addClass("success"); - - } else { - jQuery.each(data.Errors, function(nm, val) { - nm = nm.toLowerCase(); - if (nm == "emailupdate") { - $("#update-email-result").removeClass("hidden").removeClass("success").show().html(val + "
    ").addClass("error"); - } else { - $("#" + nm + "-error").removeClass("hidden").show().text(val); - } - }); - } - }) - .fail(function(xhr, status, err) { - $('#modal-spinner').modal('hide'); - $("#update-email-result").removeClass("hidden").removeClass("success").show().html(err + "
    ").addClass("error"); - }); - } - - } else if ( $(evt.target).attr("id") == "send-email") { - $.ajax(window.location.href, { - method: "POST", - data: { - action: $(evt.target).attr("id"), - }, - }) - .done(function(data) { - $('#modal-spinner').modal('hide'); - - if (data.Success) { - var msg = "Successfully sent test email.
    "; - $("#send-email-result").removeClass("hidden").removeClass("error").show().html(msg).addClass("success"); - - } else { - jQuery.each(data.Errors, function(nm, val) { - nm = nm.toLowerCase(); - if (nm == "emailsend") { - $("#send-email-result").removeClass("hidden").removeClass("success").show().html(val + "
    ").addClass("error"); - } else { - $("#" + nm + "-error").removeClass("hidden").show().text(val); - } - }); - } - }) - .fail(function(xhr, status, err) { - $('#modal-spinner').modal('hide'); - $("#send-email-result").removeClass("hidden").removeClass("success").show().html(err + "
    ").addClass("error"); - }); - } else if ( $(evt.target).attr("id") == "version-check") { $.ajax(window.location.href, { method: "POST", @@ -1547,21 +1416,6 @@ check_fqdn(); - $("#email-cb").change(function() { - $("#server").prop("required", $(this).prop("checked")); - $("#server").prop("disabled", !$(this).prop("checked")); - $("#port").prop("required", $(this).prop("checked")); - $("#port").prop("disabled", !$(this).prop("checked")); - $("#emailuser").prop("required", $(this).prop("checked")); - $("#emailuser").prop("disabled", !$(this).prop("checked")); - $("#emailpwd").prop("required", $(this).prop("checked")); - $("#emailpwd").prop("disabled", !$(this).prop("checked")); - $("#from").prop("required", $(this).prop("checked")); - $("#from").prop("disabled", !$(this).prop("checked")); - }); - - $("#email-cb").change(); - $(".account-input").keyup(function() { ok = $("#username").val() && $("#accemail").val() && $("#password").val(); $("#update-account").prop('disabled', !ok);