diff --git a/commander b/commander
index 68b94fc..4d9b8e4 100755
--- a/commander
+++ b/commander
@@ -18,11 +18,11 @@ function wait_server() {
local status=0
local cnt=0
- while [ $cnt -lt 20 ] && [ "$status" != "200" ]; do
+ while [ $cnt -lt 40 ] && [ "$status" != "200" ]; do
status=$(curl -o /dev/null -sSL --head --write-out '%{http_code}\n' $url)
let cnt=$cnt+1
- if [ $cnt -lt 10 ] && [ "$status" != "200" ]; then
- sleep 3
+ if [ "$status" != "200" ]; then
+ sleep 5
fi
done
}
@@ -55,6 +55,7 @@ case $txt in
chown -R www-data:www-data *
url=$(grep 'DEFAULT_DIRECTORY_URL =' /home/labca/acme_tiny.py | sed -e 's/.*=[ ]*//' | sed -e 's/\"//g')
wait_server $url
+ sleep 10
/home/labca/labca/renew
ln -sf /home/labca/labca/cron_d /etc/cron.d/labca
ln -sf /home/labca/labca/logrotate_d /etc/logrotate.d/labca
diff --git a/entrypoint.patch b/entrypoint.patch
index a42e1d0..aca8e21 100644
--- a/entrypoint.patch
+++ b/entrypoint.patch
@@ -2,10 +2,16 @@ diff --git a/test/entrypoint.sh b/test/entrypoint.sh
index 5ca9929..f18e1d8 100755
--- a/test/entrypoint.sh
+++ b/test/entrypoint.sh
-@@ -36,6 +36,12 @@ wait_tcp_port boulder-mysql 3306
+@@ -36,6 +36,18 @@ wait_tcp_port boulder-mysql 3306
# create the database
MYSQL_CONTAINER=1 $DIR/create_db.sh
++fl=$(pwd)/labca/setup_complete
++while [ ! -f $fl ]; do
++ echo "Waiting for $fl to appear..."
++ sleep 30
++done
++
+#softhsm2-util --show-slots
+softhsm2-util --init-token --slot 0 --label "intermediate signing key (rsa)" --pin 1234 --so-pin 5678 | /bin/true
+[ -e labca/test-ca.p8 ] && softhsm2-util --import labca/test-ca.p8 --id 333333 --force --token "intermediate signing key (rsa)" --pin 1234 --so-pin 5678 --label 'intermediate_key'
diff --git a/gui/acme.go b/gui/acme.go
index bf5ddb6..6f587d3 100644
--- a/gui/acme.go
+++ b/gui/acme.go
@@ -297,7 +297,7 @@ func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error)
if query != "" {
query = query + " UNION "
}
- query = "SELECT id, identifierValue, registrationID, status, expires FROM authz2 WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE orderID=?)"
+ query = query + "SELECT id, identifierValue, registrationID, status, expires FROM authz2 WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE orderID=?)"
}
var rows *sql.Rows
if tableExists(db, "authz") && tableExists(db, "authz2") {
@@ -514,7 +514,7 @@ func GetAuth(w http.ResponseWriter, r *http.Request, id string) (AuthShow, error
if query != "" {
query = query + " UNION "
}
- query = "SELECT id, identifierValue, registrationID, status, expires, validationError, validationRecord FROM authz2 WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE id=?)"
+ query = query + "SELECT id, identifierValue, registrationID, status, expires, validationError, validationRecord FROM authz2 WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE id=?)"
}
if tableExists(db, "authz") && tableExists(db, "authz2") {
rows, err = db.Query(query, id, id)
diff --git a/gui/apply-boulder b/gui/apply-boulder
index 49045d7..985b5bf 100755
--- a/gui/apply-boulder
+++ b/gui/apply-boulder
@@ -16,20 +16,29 @@ sed -i -e "s/\"directoryCAAIdentity\": \".*\"/\"directoryCAAIdentity\": \"$PKI_D
[ -e ../test/hostname-policy.yaml ] && cp ../test/hostname-policy.yaml ./ || true
[ -e ../boulder/test/hostname-policy.yaml ] && cp ../boulder/test/hostname-policy.yaml ./ || true
[ -e hostname-policy.json ] && rm hostname-policy.json || true
-if [ "$PKI_DOMAIN_MODE" == "lockdown" ]; then
+cat hostname-policy.yaml | tr '\n' '\r' | sed -e "s/Lockdown:.*//" | tr '\r' '\n' > hostname-policy.yaml.bak && mv hostname-policy.yaml.bak hostname-policy.yaml
+cat hostname-policy.yaml | tr '\n' '\r' | sed -e "s/Whitelist:.*//" | tr '\r' '\n' > hostname-policy.yaml.bak && mv hostname-policy.yaml.bak hostname-policy.yaml
+if [ "$PKI_DOMAIN_MODE" == "lockdown" ] && [ "$PKI_LOCKDOWN_DOMAINS" != "" ]; then
echo "Lockdown:" >> hostname-policy.yaml
echo " - \"$PKI_LOCKDOWN_DOMAINS\"" >> hostname-policy.yaml
fi
-if [ "$PKI_DOMAIN_MODE" == "whitelist" ]; then
+if [ "$PKI_DOMAIN_MODE" == "whitelist" ] && [ "$PKI_WHITELIST_DOMAINS" != "" ]; then
echo "Whitelist:" >> hostname-policy.yaml
- echo " - \"$PKI_LOCKDOWN_DOMAINS\"" >> hostname-policy.yaml
+ echo " - \"$PKI_WHITELIST_DOMAINS\"" >> hostname-policy.yaml
fi
if [ "$PKI_DOMAIN_MODE" == "lockdown" ] || [ "$PKI_DOMAIN_MODE" == "whitelist" ]; then
sed -i -e "s/^\(.*\)\(\"n_subject_common_name_included\"\)/\1\2,\n\1\"e_dnsname_not_valid_tld\"/" config/ca-a.json
sed -i -e "s/^\(.*\)\(\"n_subject_common_name_included\"\)/\1\2,\n\1\"e_dnsname_not_valid_tld\"/" config/ca-b.json
- sed -i -e "s/\( registrationOverrides:\)/ $PKI_LOCKDOWN_DOMAINS: 10000\n\1/" rate-limit-policies.yml
- echo " $PKI_LOCKDOWN_DOMAINS: 10000" >> rate-limit-policies.yml
+ REPLACEMENT=""
+ if [ "$PKI_DOMAIN_MODE" == "lockdown" ] && [ "$PKI_LOCKDOWN_DOMAINS" != "" ]; then
+ REPLACEMENT=" $PKI_LOCKDOWN_DOMAINS: 10000\n"
+ fi
+ if [ "$PKI_DOMAIN_MODE" == "whitelist" ] && [ "$PKI_WHITELIST_DOMAINS" != "" ]; then
+ REPLACEMENT=" $PKI_WHITELIST_DOMAINS: 10000\n"
+ fi
+ cat rate-limit-policies.yml | tr '\n' '\r' | sed -e "s/\(must-staple.le.wtf: 10000\).*\( registrationOverrides:\)/\1\n$REPLACEMENT\2/" | tr '\r' '\n' > rate-limit-policies.yml.bak && mv rate-limit-policies.yml.bak rate-limit-policies.yml
+ cat rate-limit-policies.yml | tr '\n' '\r' | sed -e "s/\(certificatesPerFQDNSet:.*must-staple.le.wtf: 10000\).*/\1\n$REPLACEMENT/" | tr '\r' '\n' > rate-limit-policies.yml.bak && mv rate-limit-policies.yml.bak rate-limit-policies.yml
fi
if [ "$PKI_EXTENDED_TIMEOUT" == "1" ]; then
@@ -89,3 +98,5 @@ openssl rsa -in $PKI_ROOT_CERT_BASE.key -pubout > test-root.pubkey.pem 2>/dev/nu
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in test-root.key -out test-root.p8
chown -R `ls -l PKI.md | cut -d" " -f 3,4 | sed 's/ /:/g'` .
+
+[ -f setup_complete ] || touch setup_complete
diff --git a/gui/dashboard.go b/gui/dashboard.go
index 210c3b1..0dbb465 100644
--- a/gui/dashboard.go
+++ b/gui/dashboard.go
@@ -3,13 +3,14 @@ package main
import (
"database/sql"
"fmt"
- "github.com/dustin/go-humanize"
"log"
"net/http"
"regexp"
"strconv"
"strings"
"time"
+
+ "github.com/dustin/go-humanize"
)
// Activity is a message to be shown on the dashboard, with timestamp and css class
diff --git a/gui/setup.sh b/gui/setup.sh
index 472a9e4..44cd504 100755
--- a/gui/setup.sh
+++ b/gui/setup.sh
@@ -8,7 +8,7 @@ set -e
if [ ! -e bin/labca ]; then
go mod download
- go build -o bin/labca main.go acme.go certificate.go dashboard.go
+ go build -o bin/labca
fi
bin/labca
diff --git a/gui/templates/views/manage.tmpl b/gui/templates/views/manage.tmpl
index 03631cb..f1772b5 100644
--- a/gui/templates/views/manage.tmpl
+++ b/gui/templates/views/manage.tmpl
@@ -168,7 +168,7 @@
- Next to all official domains, also allow this domain:
+ Next to all official domains, also allow this domain (whitelist):
diff --git a/gui/templates/views/setup.tmpl b/gui/templates/views/setup.tmpl
index c72c2e8..f4537d0 100644
--- a/gui/templates/views/setup.tmpl
+++ b/gui/templates/views/setup.tmpl
@@ -31,7 +31,7 @@
{{ . }}
{{ end }}
- Next to all official domains, also allow this domain:
+ Next to all official domains, also allow this domain (whitelist):
{{ with .Errors.WhitelistDomains }}
{{ . }}
diff --git a/install b/install
index ac4ff14..5e45ac8 100755
--- a/install
+++ b/install
@@ -51,6 +51,7 @@ source "$dn/utils.sh" &>/dev/null || true
cmdlineFqdn=""
cmdlineBranch=""
+fullCmdline=""
#
# Helper functions for informing the user and logging to file
@@ -195,6 +196,16 @@ clone_or_pull() {
fi
}
+# Checkout the latest release tag
+checkout_release() {
+ local branch="$1"
+ if [ "$branch" == "" ] || [ "$branch" == "master" ]; then
+ cd "$cloneDir"
+ TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
+ sudo -u labca -H git reset --hard $TAG &>>$installLog
+ fi
+}
+
# Restart the script if it was updated itself
restart_if_updated() {
local curChecksum="$1"
@@ -206,7 +217,7 @@ restart_if_updated() {
if [ "$curChecksum" != "$newChecksum" ]; then
msg_info "Restarting updated version of install script"
echo
- exec $cloneDir/install
+ exec $cloneDir/install $fullCmdline
exit $?
fi
fi
@@ -229,6 +240,7 @@ prompt_and_export() {
# Parse the command line options, if any
parse_cmdline() {
+ fullCmdline="$@"
local parsed=$(getopt --options=n:,b: --longoptions=name:,fqdn:,branch: --name "$0" -- "$@" 2>>$installLog) || msg_fatal "Could not process commandline parameters"
eval set -- "$parsed"
while true; do
@@ -252,6 +264,21 @@ parse_cmdline() {
done
}
+# Utility method to check if value looks like a host + domain
+has_domain() {
+ local dom="$1"
+
+ if [[ "$dom" =~ ^\..*$ ]]; then
+ false
+ elif [[ "$dom" =~ ^.*\.$ ]]; then
+ false
+ elif [[ "$dom" =~ ^.*\..*$ ]]; then
+ true
+ else
+ false
+ fi
+}
+
# Determine the remote address of this machine from (in order): commandline parameter,
# existing configuration or full hostname.
get_fqdn() {
@@ -259,12 +286,23 @@ get_fqdn() {
local cfgFqdn=$(grep fqdn $cfgFile 2>/dev/null | grep -v LABCA_FQDN | cut -d ":" -f 2- | tr -d " \",")
LABCA_FQDN=${cfgFqdn:-$(hostname -f)}
- if [ "$cfgFqdn" == "" ]; then
+ while [ "$cfgFqdn" == "" ]; do
if [ "$cmdlineFqdn" != "" ]; then
export LABCA_FQDN="$cmdlineFqdn"
else
prompt_and_export LABCA_FQDN "$LABCA_FQDN" "FQDN (Fully Qualified Domain Name) for this PKI host (users will use this in their browsers and clients)?"
fi
+
+ if has_domain $LABCA_FQDN; then
+ cfgFqdn="ok"
+ else
+ msg_err "FQDN must include a hostname AND a domain!"
+ cmdlineFqdn=""
+ fi
+ done
+
+ if ! has_domain $LABCA_FQDN; then
+ msg_fatal "FQDN must include a hostname AND a domain!"
fi
msg_ok "Determine web address"
@@ -490,6 +528,9 @@ config_boulder() {
sudo -u labca -H patch -p1 < $cloneDir/ra_ra.patch &>>$installLog
cp ra/ra.go "$boulderLabCADir/.backup/"
+ sudo -u labca -H patch -p1 < $cloneDir/reloader_reloader.patch &>>$installLog
+ cp reloader/reloader.go "$boulderLabCADir/.backup/"
+
sudo -u labca -H patch -p1 < $cloneDir/mail_mailer.patch &>>$installLog
cp mail/mailer.go "$boulderLabCADir/.backup/"
@@ -584,7 +625,7 @@ config_boulder() {
export PKI_LOCKDOWN_DOMAINS=$(grep lockdown $adminDir/data/config.json | grep -v domain_mode | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_WHITELIST_DOMAINS=$(grep whitelist $adminDir/data/config.json | grep -v domain_mode | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
- enabled=$(grep "email\": {" config.json -A1 | grep enable | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g')
+ enabled=$(grep "email\": {" $adminDir/data/config.json -A1 | grep enable | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g')
if [ "$enabled" == "true," ]; then
export PKI_EMAIL_SERVER=$(grep server $adminDir/data/config.json | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g')
export PKI_EMAIL_PORT=$(grep port $adminDir/data/config.json | head -1 | perl -p0e 's/.*?:\s+(.*)/\1/' | sed -e 's/\",//g' | sed -e 's/\"//g')
@@ -664,7 +705,7 @@ startup() {
wait_up $PS_MYSQL &>>$installLog
wait_up $PS_LABCA &>>$installLog
- wait_up $PS_BOULDER $PS_BOULDER_COUNT &>>$installLog
+ [ -f "$boulderLabCADir/setup_complete" ] && wait_up $PS_BOULDER $PS_BOULDER_COUNT &>>$installLog || /bin/true
msg_ok "$msg"
}
@@ -709,6 +750,7 @@ main() {
parse_cmdline "$@"
clone_or_pull "$cloneDir" "$labcaUrl" "$cmdlineBranch"
+ checkout_release "$cmdlineBranch"
restart_if_updated "$checksum"
get_fqdn
diff --git a/reloader_reloader.patch b/reloader_reloader.patch
new file mode 100644
index 0000000..cfe0623
--- /dev/null
+++ b/reloader_reloader.patch
@@ -0,0 +1,28 @@
+diff --git a/reloader/reloader.go b/reloader/reloader.go
+index d885af63..ab71babf 100644
+--- a/reloader/reloader.go
++++ b/reloader/reloader.go
+@@ -9,7 +9,7 @@ import (
+
+ // Wrap time.Tick so we can override it in tests.
+ var makeTicker = func() (func(), <-chan time.Time) {
+- t := time.NewTicker(1 * time.Second)
++ t := time.NewTicker(30 * time.Second)
+ return t.Stop, t.C
+ }
+
+@@ -55,8 +55,12 @@ func New(filename string, dataCallback func([]byte) error, errorCallback func(er
+ case <-tickChan:
+ currentFileInfo, err := os.Stat(filename)
+ if err != nil {
+- errorCallback(err)
+- continue
++ time.Sleep(10 * time.Second)
++ currentFileInfo, err = os.Stat(filename)
++ if err != nil {
++ errorCallback(err)
++ continue
++ }
+ }
+ if !currentFileInfo.ModTime().After(fileInfo.ModTime()) {
+ continue