@ -1,4 +1,4 @@
//LEAPI - ACME Certificate Renewal Control API - Copyright 2022-2024 Ruel Tmeizeh All Rights Reserved
//LEAPI - ACME Certificate Renewal Control API - Copyright 2022-2025 Ruel Tmeizeh All Rights Reserved
package main
package main
import (
import (
@ -6,6 +6,7 @@ import (
"crypto/tls"
"crypto/tls"
"encoding/json"
"encoding/json"
"errors"
"errors"
"fmt"
"io/ioutil"
"io/ioutil"
"log"
"log"
"net/http"
"net/http"
@ -19,9 +20,9 @@ import (
func writeDomains ( ) error {
func writeDomains ( ) error {
b := new ( bytes . Buffer )
b := new ( bytes . Buffer )
err := json . NewEncoder ( b ) . Encode ( domain s)
err := json . NewEncoder ( b ) . Encode ( certgroup s)
if err != nil {
if err != nil {
return errors . New ( "Couldn't encode domains lis t into JSON: " + err . Error ( ) )
return errors . New ( "Couldn't encode certgroups struc t into JSON: " + err . Error ( ) )
}
}
err = ioutil . WriteFile ( configDir + "/domains.json" , b . Bytes ( ) , 0644 )
err = ioutil . WriteFile ( configDir + "/domains.json" , b . Bytes ( ) , 0644 )
@ -47,7 +48,7 @@ func writeServers() error {
return nil
return nil
}
}
func sendFileToAllServers ( filePath string ) error {
func sendFileToAllServers ( filePath , cert_idx_str string ) error {
var theError error
var theError error
numservers := len ( servers )
numservers := len ( servers )
c := make ( chan string )
c := make ( chan string )
@ -64,7 +65,7 @@ func sendFileToAllServers(filePath string) error {
}
}
log . Println ( "Parallel execution send file to server: " + srv + "..." )
log . Println ( "Parallel execution send file to server: " + srv + "..." )
err := sendFileToServer ( filePath , srv )
err := sendFileToServer ( filePath , srv , cert_idx_str )
if err != nil {
if err != nil {
log . Println ( err . Error ( ) )
log . Println ( err . Error ( ) )
theError = err
theError = err
@ -91,17 +92,22 @@ func sendFileToAllServers(filePath string) error {
return theError //if any one or more fail, return an error for it (the last one that fails)
return theError //if any one or more fail, return an error for it (the last one that fails)
}
}
func sendFileToServer ( filePath , server string ) error {
func sendFileToServer ( filePath , server , cert_idx_str string ) error {
log . Println ( "Send file " + filePath + " to " + server + " starting..." )
log . Println ( "Send file " + filePath + " to " + server + " starting..." )
_ , fileName := path . Split ( filePath )
_ , fileName := path . Split ( filePath )
dest := strings . SplitN ( fileName , "__" , 2 ) [ 0 ] //cert__abcdef1234567890.tmpfile --> "cert"
fileTypeOrACL := strings . SplitN ( fileName , "__" , 2 ) [ 0 ] //cert__abcdef1234567890.tmpfile --> "cert"
data , err := os . Open ( filePath )
data , err := os . Open ( filePath )
if err != nil {
if err != nil {
return errors . New ( "sendFileToServer: Could not open temporary file " + filePath + ": " + err . Error ( ) )
return errors . New ( "sendFileToServer: Could not open temporary file " + filePath + ": " + err . Error ( ) )
}
}
url := syncScheme + server + ":" + syncPort + "/api/file/upload/" + dest
log . Println ( "Send file " + filePath + " to " + url + "..." )
//url := syncScheme + server + ":" + syncPort + "/api/file/upload/" + fileType + "/" + fmt.Sprintf("%02d", cert_idx)
url := syncScheme + server + ":" + syncPort + "/api/file/upload/" + fileTypeOrACL + "/" + cert_idx_str
if len ( cert_idx_str ) == 0 { // file is missing cert index number string, so is an ACL file
url = syncScheme + server + ":" + syncPort + "/api/file/upload/" + fileTypeOrACL
}
log . Println ( "Send file '" + filePath + "' to " + url + "..." )
req , err := http . NewRequest ( "PUT" , url , data )
req , err := http . NewRequest ( "PUT" , url , data )
if err != nil {
if err != nil {
@ -132,18 +138,20 @@ func sendFileToServer(filePath, server string) error {
log . Println ( errorString )
log . Println ( errorString )
return errors . New ( errorString )
return errors . New ( errorString )
}
}
log . Println ( "Upload [" + dest + "] to " + server + " success!" )
log . Println ( "Upload [" + fileTypeOrACL + "] to " + server + " success!" )
return nil
return nil
}
}
func renew ( ) error {
log . Println ( "Renew operation initiated..." )
func renew ( cert_idx int ) error {
log . Println ( "Renew operation initiated for certgroup [" + strconv . Itoa ( cert_idx ) + "] ..." )
//BUILD/SET GETSSL ENVIRONMENT VARIABLES THEN EXECUTE GETSSL
//BUILD/SET GETSSL ENVIRONMENT VARIABLES THEN EXECUTE GETSSL
//domain list
//domain list
var domainlist string
var domainlist string
cg := certgroups [ cert_idx ]
domains := cg . Domains
for _ , d := range domains {
for _ , d := range domains {
if d == appconf . PrimaryDomain { //ignore primary domain
if d == cg . PrimaryDomain { //ignore primary domain
continue
continue
}
}
domainlist = domainlist + "," + d
domainlist = domainlist + "," + d
@ -157,6 +165,9 @@ func renew() error {
log . Println ( domainlist )
log . Println ( domainlist )
}
}
// GetSSL Expected WebDAV Format: (HTTPS ONLY!)
// ";davs:davsuser:davspassword:{DOMAIN}:443:/path"
//ACL string
//ACL string
aclstring := appconf . LetsEncryptValidationPath
aclstring := appconf . LetsEncryptValidationPath
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
@ -165,7 +176,6 @@ func renew() error {
continue
continue
}
}
aclstring += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . LetsEncryptValidationPath
aclstring += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . LetsEncryptValidationPath
//aclstring += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
aclstring += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync"
aclstring += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync"
@ -182,86 +192,82 @@ func renew() error {
}
}
//Cert and key locations
//Cert and key locations
domain_cert_location := appconf . TLSCertFile
domain_cert_location := appconf . TLSCertPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
for _ , server := range servers {
for _ , server := range servers {
if server == appconf . Hostname {
if server == appconf . Hostname {
continue
continue
}
}
domain_cert_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSCertFile
//domain_cert_location += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/cert"
domain_cert_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSCertPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
domain_cert_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/cert"
// ;davs:user:pass:hostname:port:/api/file/sync/cert/{cert_idx}
domain_cert_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/cert/" + fmt . Sprintf ( "%02d" , cert_idx )
}
}
err = os . Setenv ( "DOMAIN_CERT_LOCATION" , domain_cert_location )
err = os . Setenv ( "DOMAIN_CERT_LOCATION" , domain_cert_location )
if err != nil {
if err != nil {
return errors . New ( "RENEW: error setting DOMAIN_CERT_LOCATION environment variable: " + err . Error ( ) )
return errors . New ( "RENEW: error setting DOMAIN_CERT_LOCATION environment variable: " + err . Error ( ) )
}
}
domain_key_location := appconf . TLSKeyFile
domain_key_location := appconf . TLSKeyPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".key"
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
for _ , server := range servers {
for _ , server := range servers {
if server == appconf . Hostname {
if server == appconf . Hostname {
continue
continue
}
}
domain_key_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSKeyFile
//domain_key_location += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/key"
domain_key_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSKeyPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".key"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
domain_key_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/key"
domain_key_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/key/ " + fmt . Sprintf ( "%02d" , cert_idx )
}
}
err = os . Setenv ( "DOMAIN_KEY_LOCATION" , domain_key_location )
err = os . Setenv ( "DOMAIN_KEY_LOCATION" , domain_key_location )
if err != nil {
if err != nil {
return errors . New ( "RENEW: error setting DOMAIN_KEY_LOCATION environment variable: " + err . Error ( ) )
return errors . New ( "RENEW: error setting DOMAIN_KEY_LOCATION environment variable: " + err . Error ( ) )
}
}
domain_chain_location := appconf . TLSChainFile
domain_chain_location := appconf . TLSChainPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
for _ , server := range servers {
for _ , server := range servers {
if server == appconf . Hostname {
if server == appconf . Hostname {
continue
continue
}
}
//domain_chain_location += ";ssh:" + appconf.Username + "@" + server + ":" + appconf.TLSChainFile
domain_chain_location += ";davs:leapi:" + appconf . SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/chain"
domain_chain_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSChainPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
domain_chain_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/chain"
domain_chain_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/chain/ " + fmt . Sprintf ( "%02d" , cert_idx )
}
}
err = os . Setenv ( "DOMAIN_CHAIN_LOCATION" , domain_chain_location )
err = os . Setenv ( "DOMAIN_CHAIN_LOCATION" , domain_chain_location )
if err != nil {
if err != nil {
return errors . New ( "RENEW: error setting DOMAIN_CHAIN_LOCATION environment variable: " + err . Error ( ) )
return errors . New ( "RENEW: error setting DOMAIN_CHAIN_LOCATION environment variable: " + err . Error ( ) )
}
}
domain_pem_location := appconf . TLSPEMFile
domain_full pem_location := appconf . TLSPEMPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".pem"
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
for _ , server := range servers {
for _ , server := range servers {
if server == appconf . Hostname {
if server == appconf . Hostname {
continue
continue
}
}
domain_pem_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSPEMFile
//domain_pem_location += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/pem"
domain_fullpem_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSPEMPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".pem"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
domain_pem_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/pem"
domain_full pem_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/pem/ " + fmt . Sprintf ( "%02d" , cert_idx )
}
}
err = os . Setenv ( "DOMAIN_PEM_LOCATION" , domain_pem_location )
err = os . Setenv ( "DOMAIN_PEM_LOCATION" , domain_full pem_location )
if err != nil {
if err != nil {
return errors . New ( "RENEW: error setting DOMAIN_PEM_LOCATION environment variable: " + err . Error ( ) )
return errors . New ( "RENEW: error setting DOMAIN_PEM_LOCATION environment variable: " + err . Error ( ) )
}
}
//these parameters don't seem to be respected by gettssl from environment variables, so write them to config file:
ca_cert_location := appconf . TLSCAFile
//these parameters don't seem to be respected by GetSSL from environment variables, so write them to config file:
ca_cert_location := appconf . TLSCAPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
if appconf . SyncType == "ssh" {
if appconf . SyncType == "ssh" {
for _ , server := range servers {
for _ , server := range servers {
if server == appconf . Hostname {
if server == appconf . Hostname {
continue
continue
}
}
ca_cert_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSCAFile
//ca_cert_location += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/ca"
ca_cert_location += ";ssh:" + appconf . Username + "@" + server + ":" + appconf . TLSCAPath + fmt . Sprintf ( "%02d" , cert_idx ) + ".crt"
}
}
} else { //file sync type is HTTPS
} else { //file sync type is HTTPS
ca_cert_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/ca"
ca_cert_location += ";davs:" + appconf . Username + ":" + appconf . SecretKey + ":" + appconf . Hostname + ":" + appconf . HTTPS_ServerPort + ":/api/file/sync/ca/ " + fmt . Sprintf ( "%02d" , cert_idx )
}
}
reload_command := appconf . ReloadCommand
reload_command := appconf . ReloadCommand
@ -272,7 +278,7 @@ func renew() error {
//old ssh method; requires ssh key
//old ssh method; requires ssh key
//reload_command += "; ssh " + appconf.Username + "@" + server + " '" + appconf.ReloadCommand + "'"
//reload_command += "; ssh " + appconf.Username + "@" + server + " '" + appconf.ReloadCommand + "'"
//new method; calls LEAPI to trigger reload
//new method; calls LEAPI to trigger reload
reload_command += " ; curl -s -k -X POST -H 'Authorization: Bearer " + appconf . SecretKey + "' " + syncScheme + server + "/api/reload"
reload_command += " ; curl -s -k -X POST -H 'Authorization: Bearer " + appconf . SecretKey + "' " + syncScheme + server + ":" + appconf . HTTPS_ServerPort + " /api/reload"
//reload_command += "; 'curl -s -X POST -H \\\"Authorization: Bearer " + appconf.SecretKey + "\\\" " + syncScheme + server + "/api/reload"
//reload_command += "; 'curl -s -X POST -H \\\"Authorization: Bearer " + appconf.SecretKey + "\\\" " + syncScheme + server + "/api/reload"
}
}
@ -285,17 +291,48 @@ func renew() error {
configFile = "CA=\"" + ca_server + "\"\n"
configFile = "CA=\"" + ca_server + "\"\n"
configFile += "USE_SINGLE_ACL=\"true\"\n"
configFile += "USE_SINGLE_ACL=\"true\"\n"
configFile += "CA_CERT_LOCATION=\"" + appconf . TLSCAFile + "\"\n"
configFile += "CA_CERT_LOCATION=\"" + ca_cert_location + "\"\n"
configFile += "RELOAD_CMD=\"" + reload_command + "\"\n"
configFile += "RELOAD_CMD=\"" + reload_command + "\"\n"
configFile += "RENEW_ALLOW=\"" + appconf . RenewAllow + "\"\n"
configFile += "RENEW_ALLOW=\"" + appconf . RenewAllow + "\"\n"
configFile += "CHECK_REMOTE=\"true\"\n"
configFile += "SERVER_TYPE=\"" + appconf . CheckPort + "\"\n"
configFile += "CHECK_REMOTE_WAIT=\"5\"\n"
configFile += "CHECK_REMOTE=\"false\"\n"
//configFile += "CHECK_REMOTE=\"true\"\n"
//configFile += "SERVER_TYPE=\"" + appconf.CheckPort + "\"\n"
//configFile += "CHECK_REMOTE_WAIT=\"" + strconv.Itoa(appconf.CheckWaitSec) + "\"\n"
//if this certgroup is a wildcard cert group, set the validation mode to DNS and write the DNS add scripts that call myself.
if cg . Wildcard {
configFile += "VALIDATE_VIA_DNS=true\n"
configFile += "LEAPI_URL=" + syncScheme + appconf . Hostname + ":" + syncPort + "\n"
configFile += "LEAPI_APITOKEN=" + appconf . SecretKey + "\n"
configFile += "DNS_ADD_COMMAND=" + configDir + "/dns_add_kazoo\n"
configFile += "DNS_DEL_COMMAND=" + configDir + "/dns_del_kazoo\n"
//write DNS add/del scripts
err = writeDnsScriptFile ( "add" )
if err != nil {
return errors . New ( "RENEW: " + err . Error ( ) )
}
err = writeDnsScriptFile ( "del" )
if err != nil {
return errors . New ( "RENEW: " + err . Error ( ) )
}
}
dir := configDir + "/" + cg . PrimaryDomain
//Check and create config file directory
if _ , err := os . Stat ( dir ) ; os . IsNotExist ( err ) {
//err = os.MkdirAll(configDir+"/"+cg.PrimaryDomain, os.ModeDir)
err = os . MkdirAll ( dir , 0755 )
if err != nil {
return errors . New ( "Couldn't create directory '" + dir + "': " + err . Error ( ) )
}
}
//write config file
//write config file
err = ioutil . WriteFile ( configDir + "/" + appconf . PrimaryDomain + "/getssl.cfg" , [ ] byte ( configFile ) , 0644 )
err = ioutil . WriteFile ( configDir + "/" + cg . PrimaryDomain + "/getssl.cfg" , [ ] byte ( configFile ) , 0644 )
if err != nil {
if err != nil {
return errors . New ( "Couldn't write getssl config file: " + configDir + "/" + appconf . PrimaryDomain + "/getssl.cfg" )
return errors . New ( "Couldn't write getssl config file: " + configDir + "/" + cg . PrimaryDomain + "/getssl.cfg" )
}
}
if appconf . Debug {
if appconf . Debug {
@ -307,29 +344,54 @@ func renew() error {
//RUN GETSSL
//RUN GETSSL
//first patch getssl to disable cert verification checking:
//first patch getssl to disable cert verification checking:
if appconf . Debug {
log . Println ( "First patch getssl to disable cert verification checking..." )
}
cmd := exec . Command ( "/usr/bin/sed" , "-i" , "s/NOMETER} -u/NOMETER} -k -u/g" , appconf . SrvDir + "/getssl" )
cmd := exec . Command ( "/usr/bin/sed" , "-i" , "s/NOMETER} -u/NOMETER} -k -u/g" , appconf . SrvDir + "/getssl" )
output , err := cmd . CombinedOutput ( )
output , err := cmd . CombinedOutput ( )
if err != nil {
if err != nil {
log . Println ( string ( output ) )
log . Println ( string ( output ) )
return errors . New ( "RENEW: patching of getssl to disable curl certificate verification during LEAPI sync failed: " + err . Error ( ) )
return errors . New ( "RENEW: patching of getssl to disable curl certificate verification during LEAPI sync failed: " + err . Error ( ) )
}
}
//RUN getssl on primary domain to renew
//RUN getssl on primary domain to renew
//cmd = exec.Command(appconf.SrvDir+"/getssl", "-u", "-w", appconf.SrvDir, appconf.PrimaryDomain)
return executeGetssl ( cg . PrimaryDomain )
}
func executeGetssl ( domain string ) error {
/ *
getssl := appconf . SrvDir + "/getssl -w " + appconf . SrvDir + " \"" + domain + "\""
if appconf . Debug {
getssl = appconf . SrvDir + "/getssl -d -w " + appconf . SrvDir + " \"" + domain + "\""
}
log . Println ( "Executing getssl with: '" + getssl + "'..." )
execScript := "#!/bin/sh\n"
execScript += getssl + "\n"
//write script file to run reload command[s]
err := ioutil . WriteFile ( configDir + "/execgetssl.sh" , [ ] byte ( execScript ) , 0755 )
if err != nil {
return errors . New ( "Couldn't write execgetssl.sh script file: " + configDir + "/execgetssl.sh" )
}
cmd := exec . Command ( appconf . SrvDir + "/execgetssl.sh" )
* /
log . Println ( "Executing getssl on primary domain: " + domain + "..." )
cmd := exec . Command ( appconf . SrvDir + "/getssl" , "-w" , appconf . SrvDir , domain )
if appconf . Debug {
if appconf . Debug {
cmd = exec . Command ( appconf . SrvDir + "/getssl" , "-d" , "-w" , appconf . SrvDir , appconf . PrimaryDomain )
} else {
cmd = exec . Command ( appconf . SrvDir + "/getssl" , "-w" , appconf . SrvDir , appconf . PrimaryDomain )
cmd = exec . Command ( appconf . SrvDir + "/getssl" , "-d" , "-w" , appconf . SrvDir , domain )
}
}
output , err = cmd . CombinedOutput ( )
output , err := cmd . CombinedOutput ( )
log . Println ( "====================== BEGIN GETSSL OUTPUT: ======================" )
log . Println ( string ( output ) )
log . Println ( "======================= END GETSSL OUTPUT ========================" )
if err != nil {
if err != nil {
log . Println ( "BEGIN GETSSL OUTPUT:" )
log . Println ( string ( output ) )
log . Println ( "END GETSSL OUTPUT" )
return errors . New ( "RENEW: execution of getssl failed: " + err . Error ( ) + " Check log file " + appconf . LogFile + " for more details." )
return errors . New ( "RENEW: execution of getssl failed: " + err . Error ( ) + " Check log file " + appconf . LogFile + " for more details." )
}
}
log . Println ( "BEGIN GETSSL OUTPUT:" )
log . Println ( string ( output ) )
log . Println ( "END GETSSL OUTPUT" )
return nil
return nil
}
}
@ -359,3 +421,126 @@ func reload() error {
return nil
return nil
}
}
func reloadRemote ( server string ) error {
url := syncScheme + server + ":" + syncPort + "/api/reload"
req , err := http . NewRequest ( "POST" , url , nil )
if err != nil {
log . Println ( err . Error ( ) )
return errors . New ( "Couldn't create new HTTP reload request for server: " + server )
}
req . Close = true
req . Header . Set ( "User-Agent" , myUserAgent )
req . SetBasicAuth ( appconf . Username , appconf . SecretKey )
//req.Header.Set("Authorization", "Bearer "+appconf.SecretKey)
//skip verification of cert, since the cert may not be setup properly at first
customTransport := http . DefaultTransport . ( * http . Transport ) . Clone ( )
customTransport . TLSClientConfig = & tls . Config { InsecureSkipVerify : true }
client := & http . Client { Transport : customTransport , Timeout : timeout }
//client := &http.Client{Timeout: timeout}
response , err := client . Do ( req )
if err != nil {
log . Println ( err . Error ( ) )
return errors . New ( "Couldn't send HTTP remote reload to server: " + server )
}
body , err := ioutil . ReadAll ( response . Body )
if err != nil {
log . Println ( err . Error ( ) )
return errors . New ( "Couldn't parse response body on request to server: " + server )
}
if response . StatusCode != 200 {
errorString := "Problem sending remote reload to server " + server + ". Status code: " + strconv . Itoa ( response . StatusCode ) + " Body: " + string ( body )
log . Println ( errorString )
return errors . New ( errorString )
}
return nil
}
func checkDomain ( domain , certPath string ) error {
log . Println ( "Checking for successful installation of certificate on '" + domain + "' (verify fingerprint)..." )
//could do this in go with tls.Dial but using openssl is quicker and easier
opensslLocalCertCmd := "openssl x509 -noout -fingerprint < " + certPath + " 2>/dev/null"
localCertFP , err := exec . Command ( "/bin/sh" , "-c" , opensslLocalCertCmd ) . CombinedOutput ( )
if err != nil {
log . Println ( "Error executing '" + opensslLocalCertCmd + "'. ERROR: " + err . Error ( ) + " OUTPUT: " + string ( localCertFP ) )
return errors . New ( "Local cert fingerprint check failed: " + err . Error ( ) )
}
opensslRemoteCertCmd := "openssl s_client -servername " + domain + " -connect " + domain + ":" + appconf . CheckPort + " 2>/dev/null | openssl x509 -noout -fingerprint 2>/dev/null"
remoteCertFP , err := exec . Command ( "/bin/sh" , "-c" , opensslRemoteCertCmd ) . CombinedOutput ( )
if err != nil {
log . Println ( "Error executing '" + opensslRemoteCertCmd + "'. ERROR: " + err . Error ( ) + " OUTPUT: " + string ( remoteCertFP ) )
return errors . New ( "Remote cert fingerprint check failed: " + err . Error ( ) )
}
if appconf . Debug {
log . Printf ( "Local certificate fingerprint: " + string ( localCertFP ) )
log . Printf ( "Remote certificate fingerprint: " + string ( remoteCertFP ) )
}
if string ( remoteCertFP ) != string ( localCertFP ) {
return errors . New ( "Remote certificate check failed: certificate fingerprints do not match! Local: " + string ( localCertFP ) + "Remote: " + string ( remoteCertFP ) )
}
return nil
}
func writeDnsScriptFile ( action string ) ( err error ) {
script := ` # ! / usr / bin / env bash
url = $ { LEAPI_URL : - ' ` + syncScheme + appconf.Hostname + ":" + syncPort + ` ' } # base URL
apitoken = $ { LEAPI_APITOKEN : - ' ` + appconf.SecretKey + ` ' }
fulldomain = "${1}"
challenge = "${2}"
echo "url = $url"
echo "apitoken = $apitoken"
echo "fulldomain = $fulldomain"
echo "challenge = $challenge"
# Check initial parameters
if [ [ - z "$fulldomain" ] ] ; then
echo "DNS script requires full domain name as first parameter"
exit 1
fi
if [ [ - z "$challenge" ] ] ; then
echo "DNS script requires challenge token as second parameter"
exit 1
fi
if [ [ - z "$apitoken" ] ] && [ [ - z "$password" ] ] ; then
echo "Must set LEAPI_APITOKEN in dns script, environment variable or getssl.cfg"
exit 1
fi
if [ [ - z "$url" ] ] ; then
echo "LEAPI_URL (url) parameter not set"
exit 1
fi
# txt_record = "_acme-challenge.${fulldomain}"
# command = "curl -k -H 'Authorization: Bearer $apitoken' -H 'Content-Type: application/json' -X POST ${url}/api/dns` + action + ` --data '{\"data\":{ \"domain\":\"${txt_record}\", \"challenge\":\"$challenge\"}}'"
# echo "RUNNING COMMAND: $command"
# resp = $ ( curl - k - H "Authorization: Bearer $apitoken" - H ' Content - Type : application / json ' - X POST $ { url } / api / dns ` + action + ` - d "{\"data\":{ \"domain\":\"${txt_record}\", \"challenge\":\"$challenge\"}}" )
resp = $ ( curl - k - H "Authorization: Bearer $apitoken" - H ' Content - Type : application / json ' - X POST $ { url } / api / dns ` + action + ` - d "{\"data\":{ \"domain\":\"${fulldomain}\", \"challenge\":\"$challenge\"}}" )
echo "SCRIPT RESPONSE FROM LEAPI: $resp"
if [ [ "$resp" == * \ "status\" : 200 * ] ] ; then
exit
fi
exit 1
`
//write config file
err = ioutil . WriteFile ( configDir + "/dns_" + action + "_kazoo" , [ ] byte ( script ) , 0755 )
if err != nil {
return errors . New ( "Couldn't write dns [" + action + "] script file: " + configDir + "/dns_" + action + "_kazoo" )
}
return err
}