Compare commits

...

6 Commits

6 changed files with 186 additions and 98 deletions
Split View
  1. +41
    -34
      actions.go
  2. +74
    -33
      api.go
  3. +2
    -2
      leapi.service
  4. +10
    -8
      leapi_config.json.sample
  5. +46
    -19
      main.go
  6. +13
    -2
      sync.go

+ 41
- 34
actions.go View File

@ -6,6 +6,7 @@ import (
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
@ -19,9 +20,9 @@ import (
func writeDomains() error {
b := new(bytes.Buffer)
err := json.NewEncoder(b).Encode(domains)
err := json.NewEncoder(b).Encode(certgroups)
if err != nil {
return errors.New("Couldn't encode domains list into JSON: " + err.Error())
return errors.New("Couldn't encode certgroups struct into JSON: " + err.Error())
}
err = ioutil.WriteFile(configDir+"/domains.json", b.Bytes(), 0644)
@ -47,7 +48,7 @@ func writeServers() error {
return nil
}
func sendFileToAllServers(filePath string) error {
func sendFileToAllServers(filePath, cert_idx string) error {
var theError error
numservers := len(servers)
c := make(chan string)
@ -64,7 +65,7 @@ func sendFileToAllServers(filePath string) error {
}
log.Println("Parallel execution send file to server: " + srv + "...")
err := sendFileToServer(filePath, srv)
err := sendFileToServer(filePath, srv, cert_idx)
if err != nil {
log.Println(err.Error())
theError = err
@ -91,17 +92,18 @@ func sendFileToAllServers(filePath string) error {
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 string) error {
log.Println("Send file " + filePath + " to " + server + " starting...")
_, fileName := path.Split(filePath)
dest := strings.SplitN(fileName, "__", 2)[0] //cert__abcdef1234567890.tmpfile --> "cert"
fileType := strings.SplitN(fileName, "__", 2)[0] //cert__abcdef1234567890.tmpfile --> "cert"
data, err := os.Open(filePath)
if err != nil {
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/" + fileType + "/" + cert_idx
log.Println("Send file '" + filePath + "' to " + url + "...")
req, err := http.NewRequest("PUT", url, data)
if err != nil {
@ -132,18 +134,20 @@ func sendFileToServer(filePath, server string) error {
log.Println(errorString)
return errors.New(errorString)
}
log.Println("Upload [" + dest + "] to " + server + " success!")
log.Println("Upload [" + fileType + "] to " + server + " success!")
return nil
}
func renew() error {
func renew(cert_idx int) error {
log.Println("Renew operation initiated...")
//BUILD/SET GETSSL ENVIRONMENT VARIABLES THEN EXECUTE GETSSL
//domain list
var domainlist string
cg := certgroups[cert_idx]
domains := cg.Domains
for _, d := range domains {
if d == appconf.PrimaryDomain { //ignore primary domain
if d == cg.PrimaryDomain { //ignore primary domain
continue
}
domainlist = domainlist + "," + d
@ -157,6 +161,9 @@ func renew() error {
log.Println(domainlist)
}
// GetSSL Expected WebDAV Format: (HTTPS ONLY!)
// ";davs:davsuser:davspassword:{DOMAIN}:443:/path"
//ACL string
aclstring := appconf.LetsEncryptValidationPath
if appconf.SyncType == "ssh" {
@ -165,7 +172,6 @@ func renew() error {
continue
}
aclstring += ";ssh:" + appconf.Username + "@" + server + ":" + appconf.LetsEncryptValidationPath
//aclstring += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/"
}
} else { //file sync type is HTTPS
aclstring += ";davs:" + appconf.Username + ":" + appconf.SecretKey + ":" + appconf.Hostname + ":" + appconf.HTTPS_ServerPort + ":/api/file/sync"
@ -182,86 +188,87 @@ func renew() error {
}
//Cert and key locations
domain_cert_location := appconf.TLSCertFile
domain_cert_location := appconf.TLSCertPath + fmt.Sprintf("%02d", cert_idx) + ".crt"
if appconf.SyncType == "ssh" {
for _, server := range servers {
if server == appconf.Hostname {
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
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)
if err != nil {
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" {
for _, server := range servers {
if server == appconf.Hostname {
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
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)
if err != nil {
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" {
for _, server := range servers {
if server == appconf.Hostname {
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 += ";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
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)
if err != nil {
return errors.New("RENEW: error setting DOMAIN_CHAIN_LOCATION environment variable: " + err.Error())
}
domain_pem_location := appconf.TLSPEMFile
domain_pem_location := appconf.TLSPEMPath + fmt.Sprintf("%02d", cert_idx) + ".pem"
if appconf.SyncType == "ssh" {
for _, server := range servers {
if server == appconf.Hostname {
continue
}
domain_pem_location += ";ssh:" + appconf.Username + "@" + server + ":" + appconf.TLSPEMFile
//domain_pem_location += ";davs:leapi:" + appconf.SecretKey + ":" + server + ":" + syncPort + ":/api/file/upload/pem"
domain_pem_location += ";ssh:" + appconf.Username + "@" + server + ":" + appconf.TLSPEMPath + fmt.Sprintf("%02d", cert_idx) + ".pem"
}
} else { //file sync type is HTTPS
domain_pem_location += ";davs:" + appconf.Username + ":" + appconf.SecretKey + ":" + appconf.Hostname + ":" + appconf.HTTPS_ServerPort + ":/api/file/sync/pem"
domain_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)
if err != nil {
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 + ".crt"
if appconf.SyncType == "ssh" {
for _, server := range servers {
if server == appconf.Hostname {
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
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
@ -285,7 +292,7 @@ func renew() error {
configFile = "CA=\"" + ca_server + "\"\n"
configFile += "USE_SINGLE_ACL=\"true\"\n"
configFile += "CA_CERT_LOCATION=\"" + appconf.TLSCAFile + "\"\n"
configFile += "CA_CERT_LOCATION=\"" + appconf.TLSCAPath + "\"\n"
configFile += "RELOAD_CMD=\"" + reload_command + "\"\n"
configFile += "RENEW_ALLOW=\"" + appconf.RenewAllow + "\"\n"
configFile += "CHECK_REMOTE=\"true\"\n"
@ -293,9 +300,9 @@ func renew() error {
configFile += "CHECK_REMOTE_WAIT=\"5\"\n"
//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 {
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 {
@ -316,9 +323,9 @@ func renew() error {
//RUN getssl on primary domain to renew
//cmd = exec.Command(appconf.SrvDir+"/getssl", "-u", "-w", appconf.SrvDir, appconf.PrimaryDomain)
if appconf.Debug {
cmd = exec.Command(appconf.SrvDir+"/getssl", "-d", "-w", appconf.SrvDir, appconf.PrimaryDomain)
cmd = exec.Command(appconf.SrvDir+"/getssl", "-d", "-w", appconf.SrvDir, cg.PrimaryDomain)
} else {
cmd = exec.Command(appconf.SrvDir+"/getssl", "-w", appconf.SrvDir, appconf.PrimaryDomain)
cmd = exec.Command(appconf.SrvDir+"/getssl", "-w", appconf.SrvDir, cg.PrimaryDomain)
}
output, err = cmd.CombinedOutput()
if err != nil {


+ 74
- 33
api.go View File

@ -2,6 +2,7 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
@ -28,9 +29,11 @@ func uptimeCheck(c echo.Context) error {
}
func apiRenew(c echo.Context) error {
err := renew()
if err != nil {
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
for n, _ := range certgroups {
err := renew(n)
if err != nil {
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
}
}
return c.JSON(okOut())
}
@ -43,22 +46,24 @@ func apiReload(c echo.Context) error {
return c.JSON(okOut())
}
//Receive file and store it
func apiUpload(c echo.Context) error {
fileType := c.Param("fileType")
cert_idx := c.Param("cert_idx")
r := c.Request()
var filePath string
switch fileType {
case "ca":
filePath = appconf.TLSCAFile
filePath = appconf.TLSCAPath + fmt.Sprintf("%02d", cert_idx) + ".crt"
case "chain":
filePath = appconf.TLSChainFile
filePath = appconf.TLSChainPath + fmt.Sprintf("%02d", cert_idx) + ".crt"
case "key":
filePath = appconf.TLSKeyFile
filePath = appconf.TLSKeyPath + fmt.Sprintf("%02d", cert_idx) + ".key"
case "cert":
filePath = appconf.TLSCertFile
filePath = appconf.TLSCertPath + fmt.Sprintf("%02d", cert_idx) + ".crt"
case "pem":
filePath = appconf.TLSPEMFile
filePath = appconf.TLSPEMPath + fmt.Sprintf("%02d", cert_idx) + ".pem"
default: //ACL
//return c.JSON(errorOut(http.StatusBadRequest, "Invalid filetype/URL."))
filePath = appconf.LetsEncryptValidationPath + "/" + fileType
@ -96,6 +101,7 @@ func apiUpload(c echo.Context) error {
func apiUploadSync(c echo.Context) error {
fileType := c.Param("fileType")
cert_idx := c.Param("cert_idx")
r := c.Request()
//Read the upload data
@ -103,22 +109,22 @@ func apiUploadSync(c echo.Context) error {
body, err := ioutil.ReadAll(io.LimitReader(r.Body, blimit))
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error reading post body: "+err.Error()))
return c.JSON(errorOut(http.StatusInternalServerError, "Error reading PUT body: "+err.Error()))
}
uuid := generateUUID()
filePath := appconf.SrvDir + "/" + fileType + "__" + uuid + ".tmpfile"
log.Println("Received PUT for sync to " + r.RequestURI)
log.Println("Writing to " + filePath)
//Write the file
err = ioutil.WriteFile(filePath, body, 0644)
if err != nil {
return c.JSON(errorOut(http.StatusInternalServerError, "Could not write temporary file: "+err.Error()))
}
log.Println("Received PUT for sync to " + r.RequestURI)
log.Println("Writing to " + filePath)
err = sendFileToAllServers(filePath)
err = sendFileToAllServers(filePath, cert_idx)
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error sending file "+filePath+" to other servers: "+err.Error()))
@ -131,7 +137,9 @@ func apiListDomains(c echo.Context) error {
var out APIOutput
out.Status = http.StatusOK
out.Message = "domains list"
out.Data = domains
for _, cg := range certgroups {
out.Data = append(out.Data, cg)
}
return c.JSON(out.Status, out)
}
@ -139,14 +147,35 @@ func apiPutDomain(c echo.Context) error {
domain := c.Param("domain")
//check for dups
for _, d := range domains {
if d == domain {
for _, cg := range certgroups {
if cg.PrimaryDomain == domain {
return c.JSON(errorOut(http.StatusBadRequest, "Bad request: Domain already exists."))
}
for _, d := range cg.Domains {
if d == domain {
return c.JSON(errorOut(http.StatusBadRequest, "Bad request: Domain already exists."))
}
}
}
var certgroup_slot int
//add domain to list
domains = append(domains, domain)
for n, cg := range certgroups {
if len(cg.Domains) < (appconf.MaxDomainsPerCert - 1) { //can't have more than 100 names on a single cert
cg.Domains = append(cg.Domains, domain)
certgroups[n] = cg //replace with appended version
certgroup_slot = n //set slot we need to run renewal for
}
}
if len(certgroups) == 0 { //certgroups is empty, so start fresh
var cg CertGroup
cg.PrimaryDomain = appconf.PrimaryDomain
if domain != appconf.PrimaryDomain {
cg.Domains = append(cg.Domains, domain)
}
certgroups = append(certgroups, cg)
}
//write list to disk
err := writeDomains()
@ -163,7 +192,7 @@ func apiPutDomain(c echo.Context) error {
}
//renew cert
err = renew()
err = renew(certgroup_slot)
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
@ -175,14 +204,20 @@ func apiPutDomain(c echo.Context) error {
func apiDeleteDomain(c echo.Context) error {
deleteDomain := c.Param("domain")
var newlist []string
for _, d := range domains {
if d != deleteDomain {
newlist = append(newlist, d)
var certgroup_slot int
for n, cg := range certgroups {
for _, d := range cg.Domains {
if d == deleteDomain {
certgroup_slot = n //save the slot this deleted domain is in
} else { //rebuild the list with domains not deleted
newlist = append(newlist, d)
}
}
cg.Domains = newlist
certgroups[n] = cg //replace this slot
}
domains = newlist
//write list to disk
err := writeDomains()
if err != nil {
@ -198,7 +233,7 @@ func apiDeleteDomain(c echo.Context) error {
}
//renew cert
err = renew()
err = renew(certgroup_slot)
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
@ -211,7 +246,9 @@ func apiListServers(c echo.Context) error {
var out APIOutput
out.Status = http.StatusOK
out.Message = "servers list"
out.Data = servers
for _, server := range servers {
out.Data = append(out.Data, server)
}
return c.JSON(out.Status, out)
}
@ -243,10 +280,12 @@ func apiPutServer(c echo.Context) error {
}
//renew cert
err = renew()
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
for n, _ := range certgroups {
err = renew(n)
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
}
}
return c.JSON(okOut())
@ -278,10 +317,12 @@ func apiDeleteServer(c echo.Context) error {
}
//renew cert
err = renew()
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
for n, _ := range certgroups {
err = renew(n)
if err != nil {
log.Println(err.Error())
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
}
}
return c.JSON(okOut())


+ 2
- 2
leapi.service View File

@ -6,9 +6,9 @@ After=network.target
[Service]
Type=simple
#ExecStartPre=/bin/sh -c 'until ping -c1 8.8.8.8 ; do sleep 1; done;'
#ExecStartPre=
ExecStart=/opt/leapi/leapi
ExecReload=/bin/kill -HUP $MAINPID
ExecReload=/bin/kill -USR1 $MAINPID
Restart=on-failure
[Install]


+ 10
- 8
leapi_config.json.sample View File

@ -1,7 +1,7 @@
//RuhNet LEAPI Config file
//configDir set by environment variable LEAPI_CONFDIR, otherwise assumed to be /opt/leapi or ./
{
"hostname":"web1.mydomain.net", //hostname or IP of this particular server; must match the server you add to LEAPI. You can use "-" to use the system hostname (must be resolvable by other LEAPI systems).
"hostname":"web1.mydomain.net", //hostname or IP of this particular server; MUST match the server you add to LEAPI. You can use "-" to use the system hostname (must be resolvable by other LEAPI systems).
"primary_domain":"mydomain.net", //the main base domain that is always present
"srv_dir":"/opt/leapi", //LEAPI installed directory
"sync_type":"https", //method of transferring files between LEAPI hosts. "ssh" or "https"
@ -9,13 +9,15 @@
"log_file":"/var/log/leapi.log",
"debug":false,
"frontend_url":"admin.mydomain.net", //the frontend URL, if any (for CORS). Use "-" if none.
"http_server_port":"80", //set to 80 if you aren't using a separate web server
"https_server_port":"-", //set to "-" to disable HTTPS (mainly useful for initial setup)
"tls_cert_path":"/etc/ssl/cert.crt",
"tls_key_path":"/etc/ssl/privkey.key",
"tls_chain_path":"/etc/ssl/chain.crt",
"tls_pem_path":"/etc/ssl/domain.pem",
"tls_ca_path":"/etc/ssl/ca.crt",
"http_server_port":"80", //set to 80 if you are not using a separate web server or proxy. "-" will assume port 80.
"https_server_enable":false, //set to false to disable HTTPS listener (for initial setup, or for using a separate web server/proxy)
"https_server_port":"-", //the port your HTTPS server is running on, whether LEAPI or an external web server/proxy. Set to "-" for default (port 443)
"tls_cert_path_prefix":"/etc/ssl/cert", //file paths DO NOT INCLUDE EXTENSION. "/etc/ssl/cert" will write files "/etc/ssl/cert01.crt", "/etc/ssl/cert02.crt", etc.
"tls_key_path_prefix":"/etc/ssl/privkey",
"tls_chain_path_prefix":"/etc/ssl/chain",
"tls_pem_path_prefix":"/etc/ssl/domain",
"tls_ca_path_prefix":"/etc/ssl/ca",
"max_domains_per_cert":100, //100 max
"letsencrypt_validation_path":"-", //if "-", LEAPI handles this and you don't use a separate web server
"renew_allow_days":"70",
"reload_command":"systemctl reload leapi ; systemctl restart nginx",


+ 46
- 19
main.go View File

@ -23,7 +23,7 @@ import (
"github.com/labstack/echo/v4/middleware"
)
const version string = "1.1.1"
const version string = "1.2.0"
const serverVersion string = "RuhNet LE API v" + version
const apiVersion int = 1
const website string = "https://ruhnet.co"
@ -35,7 +35,7 @@ var appconf LEAPIConfig
var startupTime time.Time
var configDir string
var domains []string
var certgroups []CertGroup
var servers []string
var syncScheme string = "http://"
var syncPort string
@ -60,11 +60,13 @@ type LEAPIConfig struct {
Debug bool `json:"debug"`
HTTP_ServerPort string `json:"http_server_port"`
HTTPS_ServerPort string `json:"https_server_port"`
TLSCertFile string `json:"tls_cert_path"`
TLSKeyFile string `json:"tls_key_path"`
TLSChainFile string `json:"tls_chain_path"`
TLSPEMFile string `json:"tls_pem_path"`
TLSCAFile string `json:"tls_ca_path"`
HTTPS_ServerEnable bool `json:"https_server_enable"`
TLSCertPath string `json:"tls_cert_path_prefix"`
TLSKeyPath string `json:"tls_key_path_prefix"`
TLSChainPath string `json:"tls_chain_path_prefix"`
TLSPEMPath string `json:"tls_pem_path_prefix"`
TLSCAPath string `json:"tls_ca_path_prefix"`
MaxDomainsPerCert int `json:"max_domains_per_cert"` //can't have more than 100 names on a single cert
FrontEndURL string `json:"frontend_url"`
PrimaryDomain string `json:"primary_domain"`
LetsEncryptValidationPath string `json:"letsencrypt_validation_path"`
@ -75,6 +77,12 @@ type LEAPIConfig struct {
CheckPort string `json:"check_port"`
}
type CertGroup struct {
//CertPrefix string `json:"cert_prefix"`
PrimaryDomain string `json:"primary_domain"`
Domains []string `json:"domains"`
}
type UpOut struct {
Up bool `json:"up,omitempty"`
StartTime time.Time `json:"start_time,omitempty"`
@ -82,9 +90,9 @@ type UpOut struct {
}
type APIOutput struct {
Status int `json:"status,omitempty"`
Message string `json:"message,omitempty"`
Data []string `json:"data,omitempty"`
Status int `json:"status,omitempty"`
Message string `json:"message,omitempty"`
Data []interface{} `json:"data,omitempty"`
}
type keypairReloader struct {
@ -164,7 +172,7 @@ func main() {
}
defer jsonFile.Close()
fileBytes, err := ioutil.ReadAll(jsonFile)
err = json.Unmarshal(fileBytes, &domains)
err = json.Unmarshal(fileBytes, &certgroups)
if err != nil {
log.Fatal("Could not parse domains.json file: " + err.Error())
}
@ -185,8 +193,21 @@ func main() {
}
}
//set ports to defaults if "-" or zero
if appconf.HTTP_ServerPort == "-" || appconf.HTTP_ServerPort == "0" {
appconf.HTTP_ServerPort = "80"
}
if appconf.HTTPS_ServerPort == "-" || appconf.HTTPS_ServerPort == "0" {
appconf.HTTPS_ServerPort = "443"
}
//set sync port
syncPort = appconf.HTTP_ServerPort
if appconf.LetsEncryptValidationPath == "-" {
if appconf.SyncType == "https" {
syncPort = appconf.HTTPS_ServerPort
}
if appconf.LetsEncryptValidationPath == "-" || appconf.LetsEncryptValidationPath == "" {
appconf.LetsEncryptValidationPath = appconf.SrvDir + "/acme-challenge"
}
@ -268,27 +289,33 @@ func main() {
apiFile.OPTIONS("/upload/:fileType", apiUpload)
apiFile.PUT("/upload/:fileType", apiUpload)
apiFile.OPTIONS("/upload/:fileType/:cert_idx", apiUpload)
apiFile.PUT("/upload/:fileType/:cert_idx", apiUpload)
apiFile.OPTIONS("/sync/:fileType", apiUploadSync)
apiFile.PUT("/sync/:fileType", apiUploadSync)
apiFile.OPTIONS("/sync/:fileType/:cert_idx", apiUploadSync)
apiFile.PUT("/sync/:fileType/:cert_idx", apiUploadSync)
/////////////////////////////////////////////
// HTTP SERVERS CONFIG:
//TLS Server
if appconf.HTTPS_ServerPort != "-" { //disable HTTPS if port is zero
if appconf.HTTPS_ServerEnable {
syncScheme = "https://"
syncPort = appconf.HTTPS_ServerPort
keyPath := appconf.TLSKeyPath + "00.key"
certPath := appconf.TLSChainPath + "00.crt"
//certPair, err := tls.LoadX509KeyPair(appconf.TLSCertificateFile, appconf.TLSKeyFile)
if !fileExists(appconf.TLSChainFile) || !fileExists(appconf.TLSKeyFile) {
//certPair, err := tls.LoadX509KeyPair(appconf.TLSCertificateFile, appconf.TLSKeyPath)
if !fileExists(certPath) || !fileExists(keyPath) {
fmt.Println("Provided certificate and/or key file does not exist! Terminating.")
log.Fatal("Provided certificate and/or key file does not exist! Terminating.")
}
//Create loader for cert files
kpr, err := NewKeypairReloader(appconf.TLSChainFile, appconf.TLSKeyFile)
kpr, err := NewKeypairReloader(certPath, keyPath)
if err != nil {
log.Fatal(err)
}
@ -395,10 +422,10 @@ func NewKeypairReloader(certPath, keyPath string) (*keypairReloader, error) {
result.cert = &cert
go func() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
signal.Notify(c, syscall.SIGUSR1)
for range c {
log.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q", appconf.TLSChainFile, appconf.TLSKeyFile)
fmt.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q\n", appconf.TLSChainFile, appconf.TLSKeyFile)
log.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q", certPath, keyPath)
fmt.Printf("Received SIGHUP, reloading TLS certificate and key from %q and %q\n", certPath, keyPath)
if err := result.maybeReload(); err != nil {
log.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err)
fmt.Printf("Keeping old TLS certificate because the new one could not be loaded: %v", err)


+ 13
- 2
sync.go View File

@ -123,7 +123,12 @@ func syncServersFromHost(host string) error {
log.Println(err.Error())
return errors.New("Couldn't parse response body from host " + host + ": " + err.Error())
}
servers = result.Data
for _, data := range result.Data {
switch server := data.(type) {
case string:
servers = append(servers, server)
}
}
err = writeServers()
if err != nil {
@ -134,6 +139,7 @@ func syncServersFromHost(host string) error {
return nil
}
// Get domains from server and write what is received to this server's domains.json file.
func syncDomainsFromHost(host string) error {
var theError error
req, err := http.NewRequest("GET", syncScheme+host+":"+syncPort+"/api/domains", nil)
@ -171,7 +177,12 @@ func syncDomainsFromHost(host string) error {
log.Println(err.Error())
return errors.New("Couldn't parse response body from host " + host + ": " + err.Error())
}
domains = result.Data
for _, data := range result.Data {
switch cg := data.(type) {
case interface{}:
certgroups = append(certgroups, cg.(CertGroup))
}
}
err = writeDomains()
if err != nil {


Loading…
Cancel
Save