Determine issuer NameID value so we can set the correct AIA URL (#35)

This commit is contained in:
Arjan H
2022-07-31 16:42:47 +02:00
parent eb892ba54a
commit 28553dac91
8 changed files with 140 additions and 68 deletions

View File

@@ -64,6 +64,10 @@ if [ "$PKI_DOMAIN_MODE" == "lockdown" ] || [ "$PKI_DOMAIN_MODE" == "whitelist" ]
cat rate-limit-policies.yml | tr '\n' '\r' | sed -e "s|\(certificatesPerFQDNSet:.*must-staple.le.wtf: 10000\).*\(certificatesPerFQDNSetFast:.*\)|\1\n${REPLACEMENT}rateLimitsURL: http://$PKI_FQDN/rate-limits\n\2|" | tr '\r' '\n' > rate-limit-policies.yml.bak && mv rate-limit-policies.yml.bak rate-limit-policies.yml
fi
sed -i -e "s|\"issuerURL\": \".*\"|\"issuerURL\": \"http://$PKI_FQDN/aia/issuer/$PKI_ISSUER_NAME_ID\"|" config/ca-a.json
sed -i -e "s|\"issuerURL\": \".*\"|\"issuerURL\": \"http://$PKI_FQDN/aia/issuer/$PKI_ISSUER_NAME_ID\"|" config/ca-b.json
if [ "$PKI_EXTENDED_TIMEOUT" == "1" ]; then
sed -i -e "s/\"timeout\": \"15s\"/\"timeout\": \"30s\"/" config/ca-a.json
sed -i -e "s/\"timeout\": \"15s\"/\"timeout\": \"30s\"/" config/ca-b.json

View File

@@ -102,7 +102,7 @@ func reportError(err error) error {
stop := len(lines)
for i := 0; i < len(lines); i++ {
if strings.Index(lines[i], ".ServeHTTP(") >= 0 {
if strings.Contains(lines[i], ".ServeHTTP(") {
stop = i
break
}
@@ -218,14 +218,14 @@ func (ci *CertificateInfo) ImportPkcs12(tmpFile string, tmpKey string, tmpCert s
}
if out, err := exeCmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil {
if strings.Index(string(out), "invalid password") >= 0 {
if strings.Contains(string(out), "invalid password") {
return errors.New("incorrect password")
}
return reportError(err)
}
if out, err := exeCmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil {
if strings.Index(string(out), "invalid password") >= 0 {
if strings.Contains(string(out), "invalid password") {
return errors.New("incorrect password")
}
@@ -311,7 +311,7 @@ func (ci *CertificateInfo) Upload(path string, certBase string, tmpKey string, t
}
if out, err := exeCmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil {
if strings.Index(string(out), ":bad decrypt:") >= 0 {
if strings.Contains(string(out), ":bad decrypt:") {
return errors.New("incorrect password")
}
@@ -349,7 +349,7 @@ func (ci *CertificateInfo) ImportCerts(path string, rootCert string, rootKey str
rootSubject = string(r[0 : len(r)-1])
fmt.Printf("Import root with subject '%s'\n", rootSubject)
r, err = exeCmd("openssl pkey -noout -in " + rootKey)
_, err = exeCmd("openssl pkey -noout -in " + rootKey)
if err != nil {
return reportError(err)
}
@@ -394,7 +394,7 @@ func (ci *CertificateInfo) ImportCerts(path string, rootCert string, rootKey str
return errors.New("issuer not issued by our Root CA")
}
r, err = exeCmd("openssl pkey -noout -in " + issuerKey)
_, err = exeCmd("openssl pkey -noout -in " + issuerKey)
if err != nil {
return reportError(err)
}
@@ -577,8 +577,6 @@ func exeCmd(cmd string) ([]byte, error) {
out, err := exec.Command(head, parts...).CombinedOutput()
if err != nil {
fmt.Print(fmt.Sprint(err) + ": " + string(out))
} else {
//fmt.Println(string(out))
}
return out, err
}

View File

@@ -86,10 +86,10 @@ func _parseLine(line string, loc *time.Location) Activity {
if idx > -1 {
message = message[0:idx]
}
if strings.Index(message, "Checked CAA records for") > -1 {
if strings.Contains(message, "Checked CAA records for") {
message = message[0:strings.Index(message, ",")]
}
if strings.Index(message, "Validation result") > -1 {
if strings.Contains(message, "Validation result") {
message = message[0:30]
}
idx = strings.Index(message, " csr=[")
@@ -108,16 +108,16 @@ func _parseLine(line string, loc *time.Location) Activity {
if idx > -1 {
message = message[0:idx]
}
if strings.Index(message, "Certificate request - ") > -1 {
if strings.Contains(message, "Certificate request - ") {
idx = strings.Index(message, " JSON={")
if idx > -1 {
message = message[0:idx]
}
}
if strings.Index(message, "failed to complete security handshake") > -1 {
if strings.Contains(message, "failed to complete security handshake") {
activity.Class = "warning"
}
if strings.Index(message, "failed to receive the preface from client") > -1 {
if strings.Contains(message, "failed to receive the preface from client") {
activity.Class = "warning"
}
activity.Message = message

View File

@@ -4,11 +4,14 @@ import (
"bufio"
"bytes"
"context"
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"html/template"
@@ -16,6 +19,7 @@ import (
"io/ioutil"
"log"
"math"
"math/big"
"net"
"net/http"
"os"
@@ -137,9 +141,9 @@ func (reg *User) Validate(isNew bool, isChange bool) bool {
}
if isNew || isChange {
re := regexp.MustCompile(".+@.+\\..+")
re := regexp.MustCompile(`.+@.+\..+`)
matched := re.Match([]byte(reg.Email))
if matched == false {
if !matched {
reg.Errors["Email"] = "Please enter a valid email address"
}
}
@@ -1386,6 +1390,27 @@ func _buildCI(r *http.Request, session *sessions.Session, isRoot bool) *Certific
return ci
}
func issuerNameID(certfile string) (int64, error) {
cf, err := ioutil.ReadFile(certfile)
if err != nil {
log.Printf("issuerNameID: could not read cert file: %v", err)
return 0, err
}
cpb, _ := pem.Decode(cf)
crt, err := x509.ParseCertificate(cpb.Bytes)
if err != nil {
log.Printf("issuerNameID: could not parse x509 file: %v", err)
return 0, err
}
// From issuance/issuance.go : func truncatedHash
h := crypto.SHA1.New()
h.Write(crt.RawSubject)
s := h.Sum(nil)
return int64(big.NewInt(0).SetBytes(s[:7]).Int64()), nil
}
func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot bool) bool {
path := "data/"
if !isRoot {
@@ -1452,6 +1477,16 @@ func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot
return false
}
if !ci.IsRoot {
nameID, err := issuerNameID(path + certBase + ".pem")
if err == nil {
viper.Set("issuer_name_id", nameID)
viper.WriteConfig()
} else {
log.Printf("_certCreate: could not calculate IssuerNameID: %v", err)
}
}
if viper.Get("labca.organization") == nil {
viper.Set("labca.organization", ci.Organization)
viper.WriteConfig()
@@ -1478,24 +1513,6 @@ func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot
return true
}
func _parseLinuxIPRouteShow(output []byte) (net.IP, error) {
// Linux '/usr/bin/ip route show' format looks like this:
// default via 192.168.178.1 dev wlp3s0 metric 303
// 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303
lines := strings.Split(string(output), "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) >= 3 && fields[0] == "default" {
ip := net.ParseIP(fields[2])
if ip != nil {
return ip, nil
}
}
}
return nil, errors.New("no gateway found")
}
func _hostCommand(w http.ResponseWriter, r *http.Request, command string, params ...string) bool {
conn, err := net.Dial("tcp", "control:3030")
if err != nil {
@@ -1563,6 +1580,7 @@ func _applyConfig() error {
os.Setenv("PKI_DOMAIN_MODE", viper.GetString("labca.domain_mode"))
os.Setenv("PKI_LOCKDOWN_DOMAINS", viper.GetString("labca.lockdown"))
os.Setenv("PKI_WHITELIST_DOMAINS", viper.GetString("labca.whitelist"))
os.Setenv("PKI_ISSUER_NAME_ID", viper.GetString("issuer_name_id"))
if viper.GetBool("labca.extended_timeout") {
os.Setenv("PKI_EXTENDED_TIMEOUT", "1")
} else {
@@ -1698,7 +1716,7 @@ func _setupAdminUser(w http.ResponseWriter, r *http.Request) bool {
// Restore a backup file
if isMultipart {
reg := &User{
Errors: make(map[string]string),
Errors: make(map[string]string),
RequestBase: r.Header.Get("X-Request-Base"),
}
file, header, err := r.FormFile("file")
@@ -1918,14 +1936,14 @@ func setupHandler(w http.ResponseWriter, r *http.Request) {
// 3. Setup root CA certificate
if !_certCreate(w, r, "root-ca", true) {
// Cleanup the cert (if it even exists) so we will retry on the next run
_ := os.Remove("data/root-ca.pem")
os.Remove("data/root-ca.pem")
return
}
// 4. Setup issuer certificate
if !_certCreate(w, r, "ca-int", false) {
// Cleanup the cert (if it even exists) so we will retry on the next run
_ := os.Remove("data/issuer/ca-int.pem")
os.Remove("data/issuer/ca-int.pem")
return
}
@@ -2083,7 +2101,7 @@ func accountsHandler(w http.ResponseWriter, r *http.Request) {
Accounts, err := GetAccounts(w, r)
if err == nil {
render(w, r, "list:accounts", map[string]interface{}{"List": Accounts, "Title": "ACME"})
render(w, r, "list:accounts", map[string]interface{}{"List": Accounts, "Title": "ACME"})
}
}

17
install
View File

@@ -455,6 +455,7 @@ static_web() {
[ -d /home/labca/nginx_data/conf.d ] || mkdir -p /home/labca/nginx_data/conf.d
[ -d /home/labca/nginx_data/ssl ] || mkdir -p /home/labca/nginx_data/ssl
cp $cloneDir/nginx.conf /home/labca/nginx_data/conf.d/labca.conf
cp $cloneDir/proxy.conf /home/labca/nginx_data/conf.d/proxy.conf
if [ -f "$boulderLabCADir/setup_complete" ]; then
perl -i -p0e 's/\n # BEGIN temporary redirect\n location = \/ \{\n return 302 \/admin\/;\n }\n # END temporary redirect\n//igs' /home/labca/nginx_data/conf.d/labca.conf
fi
@@ -646,10 +647,6 @@ config_boulder() {
sed -i -e "s/5001/443/g" config/va-remote-b.json
sed -i -e "s/5002/80/g" config/va-remote-b.json
sed -i -e "s|https://boulder:4431/terms/v7|https://$LABCA_FQDN/terms/v1|" config/wfe2.json
sed -i -e "s|http://boulder:4430/acme/issuer-cert|http://$LABCA_FQDN/certs/ca-int.der|" config/ca-a.json
sed -i -e "s|http://boulder:4430/acme/issuer-cert|http://$LABCA_FQDN/certs/ca-int.der|" config/ca-b.json
sed -i -e "s|http://127.0.0.1:4000/acme/issuer-cert|http://$LABCA_FQDN/certs/ca-int.der|" config/ca-a.json
sed -i -e "s|http://127.0.0.1:4000/acme/issuer-cert|http://$LABCA_FQDN/certs/ca-int.der|" config/ca-b.json
sed -i -e "s|http://boulder:4430/acme/issuer-cert|http://$LABCA_FQDN/acme/issuer-cert|" config/wfe2.json
sed -i -e "s|http://127.0.0.1:4000/acme/issuer-cert|https://$LABCA_FQDN/acme/issuer-cert|" config/wfe2.json
sed -i -e "s|letsencrypt/boulder|hakwerk/labca|" config/wfe2.json
@@ -661,8 +658,6 @@ config_boulder() {
sed -i -e "s|1.2.3.4|1.3.6.1.4.1.44947.1.1.1|g" config/ca-b.json
perl -i -p0e "s/(\s+\"crlURL\":[^\n]*)//igs" config/ca-a.json
perl -i -p0e "s/(\s+\"crlURL\":[^\n]*)//igs" config/ca-b.json
sed -i -e "s/Do What Thou Wilt/This PKI is only meant for internal (lab) usage; do NOT use this on the open internet\!/g" config/ca-a.json
sed -i -e "s/Do What Thou Wilt/This PKI is only meant for internal (lab) usage; do NOT use this on the open internet\!/g" config/ca-b.json
sed -i -e "s/ocspURL.Path = encodedReq/ocspURL.Path += encodedReq/" ocsp/helper/helper.go
sed -i -e "s/\"dnsTimeout\": \".*\"/\"dnsTimeout\": \"3s\"/" config/ra.json
sed -i -e "s/\"dnsTimeout\": \".*\"/\"dnsTimeout\": \"3s\"/" config/va.json
@@ -693,6 +688,16 @@ config_boulder() {
export PKI_DOMAIN_MODE=$(grep domain_mode $adminDir/data/config.json | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
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')
export PKI_ISSUER_NAME_ID=$(grep issuer_name_id $adminDir/data/config.json | sed -e 's/.*:[ ]*//' | sed -e 's/\",//g' | sed -e 's/\"//g')
if [ -z "$PKI_ISSUER_NAME_ID" ] && [ -e "$adminDir/data/issuer/ca-int.pem" ]; then
local img=$(grep "&boulder_image" $boulderDir/docker-compose.yml | sed -e "s/.*boulder_image \(.*\)/\1/")
eval img=$img
docker run --rm -v $cloneDir/utils:/utils -w /utils $img go build nameidtool.go &>>$installLog
nmid=$($cloneDir/utils/nameidtool $adminDir/data/issuer/ca-int.pem)
if [ $? == 0 ]; then
export PKI_ISSUER_NAME_ID=$nmid
fi
fi
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

View File

@@ -14,15 +14,17 @@ server {
return 301 https://$host$request_uri;
}
location /aia/issuer {
include conf.d/proxy.conf;
proxy_pass http://boulder:4001;
}
location /directory {
return 301 https://$host$request_uri;
}
location /ocsp/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
location /ocsp {
include conf.d/proxy.conf;
proxy_pass http://boulder:4002/;
}
@@ -55,20 +57,14 @@ server {
}
location /admin/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
include conf.d/proxy.conf;
proxy_set_header X-Request-Base "/admin";
proxy_pass http://labca:3000/;
error_page 502 504 /502.html;
}
location /admin/ws {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
include conf.d/proxy.conf;
proxy_set_header X-Request-Base "/admin";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
@@ -76,26 +72,27 @@ server {
}
location /acme/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
include conf.d/proxy.conf;
proxy_pass http://boulder:4001;
}
location /directory {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
include conf.d/proxy.conf;
proxy_pass http://boulder:4001;
}
location /build {
include conf.d/proxy.conf;
proxy_pass http://boulder:4001;
}
location /aia/issuer {
include conf.d/proxy.conf;
proxy_pass http://boulder:4001;
}
location /ocsp/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
include conf.d/proxy.conf;
proxy_pass http://boulder:4002/;
}

4
proxy.conf Normal file
View File

@@ -0,0 +1,4 @@
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

46
utils/nameidtool.go Normal file
View File

@@ -0,0 +1,46 @@
package main
import (
"crypto"
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"os"
)
func issuerNameID(certfile string) (int64, error) {
cf, err := ioutil.ReadFile(certfile)
if err != nil {
fmt.Printf("issuerNameID: could not read cert file: %v", err)
return 0, err
}
cpb, _ := pem.Decode(cf)
crt, err := x509.ParseCertificate(cpb.Bytes)
if err != nil {
fmt.Printf("issuerNameID: could not parse x509 file: %v", err)
return 0, err
}
// From issuance/issuance.go : func truncatedHash
h := crypto.SHA1.New()
h.Write(crt.RawSubject)
s := h.Sum(nil)
return int64(big.NewInt(0).SetBytes(s[:7]).Int64()), nil
}
func main() {
if len(os.Args[1:]) < 1 {
fmt.Printf("Usage:\n %s <certificate.pem>\n", os.Args[0])
os.Exit(1)
}
nameID, err := issuerNameID(os.Args[1])
if err != nil {
fmt.Printf("Error: %v\n", err)
os.Exit(1)
}
fmt.Println(nameID)
}