//LEAPI - ACME Certificate Renewal Control API - Copyright 2022-2024 Ruel Tmeizeh All Rights Reserved
|
|
package main
|
|
|
|
import (
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
/////////////////////////////////////////////
|
|
///// API ROUTE FUNCTIONS
|
|
/////////////////////////////////////////////
|
|
func nothingResponse(c echo.Context) error {
|
|
return c.NoContent(http.StatusNotFound)
|
|
}
|
|
|
|
func uptimeCheck(c echo.Context) error {
|
|
if c.Request().Method == http.MethodHead {
|
|
return c.NoContent(http.StatusOK)
|
|
}
|
|
//return c.String(http.StatusOK, "{\"up\":true}")
|
|
return c.JSON(http.StatusOK, uptime())
|
|
}
|
|
|
|
func apiRenew(c echo.Context) error {
|
|
err := renew()
|
|
if err != nil {
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
|
|
}
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiReload(c echo.Context) error {
|
|
err := reload()
|
|
if err != nil {
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error reloading services: "+err.Error()))
|
|
}
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiUpload(c echo.Context) error {
|
|
fileType := c.Param("fileType")
|
|
r := c.Request()
|
|
|
|
var filePath string
|
|
switch fileType {
|
|
case "ca":
|
|
filePath = appconf.TLSCAFile
|
|
case "chain":
|
|
filePath = appconf.TLSChainFile
|
|
case "key":
|
|
filePath = appconf.TLSKeyFile
|
|
case "cert":
|
|
filePath = appconf.TLSCertFile
|
|
case "pem":
|
|
filePath = appconf.TLSPEMFile
|
|
default: //ACL
|
|
//return c.JSON(errorOut(http.StatusBadRequest, "Invalid filetype/URL."))
|
|
filePath = appconf.LetsEncryptValidationPath + "/" + fileType
|
|
}
|
|
|
|
directory, _ := path.Split(filePath)
|
|
|
|
//Check and create directory
|
|
if _, err := os.Stat(directory); os.IsNotExist(err) {
|
|
err = os.MkdirAll(directory, 0755)
|
|
if err != nil {
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Filetype "+fileType+" directory does not exist, and could not create: "+err.Error()))
|
|
}
|
|
}
|
|
|
|
//Read the upload data
|
|
var blimit int64 = 102400 //100k max upload size
|
|
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()))
|
|
}
|
|
|
|
//Write the file
|
|
err = ioutil.WriteFile(filePath, body, 0644)
|
|
if err != nil {
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Could not write file: "+err.Error()))
|
|
}
|
|
|
|
log.Println("Received PUT to " + r.RequestURI)
|
|
log.Println("Writing to " + filePath)
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiUploadSync(c echo.Context) error {
|
|
fileType := c.Param("fileType")
|
|
r := c.Request()
|
|
|
|
//Read the upload data
|
|
var blimit int64 = 102400 //100k max upload size
|
|
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()))
|
|
}
|
|
|
|
uuid := generateUUID()
|
|
filePath := appconf.SrvDir + "/" + fileType + "__" + uuid + ".tmpfile"
|
|
|
|
//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)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error sending file "+filePath+" to other servers: "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiListDomains(c echo.Context) error {
|
|
var out APIOutput
|
|
out.Status = http.StatusOK
|
|
out.Message = "domains list"
|
|
out.Data = domains
|
|
return c.JSON(out.Status, out)
|
|
}
|
|
|
|
func apiPutDomain(c echo.Context) error {
|
|
domain := c.Param("domain")
|
|
|
|
//check for dups
|
|
for _, d := range domains {
|
|
if d == domain {
|
|
return c.JSON(errorOut(http.StatusBadRequest, "Bad request: Domain already exists."))
|
|
}
|
|
}
|
|
|
|
//add domain to list
|
|
domains = append(domains, domain)
|
|
|
|
//write list to disk
|
|
err := writeDomains()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error writing domains list to disk: "+err.Error()))
|
|
}
|
|
|
|
//sync with other servers
|
|
err = syncAllServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing to other servers: "+err.Error()))
|
|
}
|
|
|
|
//renew cert
|
|
err = renew()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiDeleteDomain(c echo.Context) error {
|
|
deleteDomain := c.Param("domain")
|
|
var newlist []string
|
|
for _, d := range domains {
|
|
if d != deleteDomain {
|
|
newlist = append(newlist, d)
|
|
}
|
|
}
|
|
|
|
domains = newlist
|
|
|
|
//write list to disk
|
|
err := writeDomains()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error writing domains list to disk: "+err.Error()))
|
|
}
|
|
|
|
//sync with other servers
|
|
err = syncAllServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing to other servers: "+err.Error()))
|
|
}
|
|
|
|
//renew cert
|
|
err = renew()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiListServers(c echo.Context) error {
|
|
var out APIOutput
|
|
out.Status = http.StatusOK
|
|
out.Message = "servers list"
|
|
out.Data = servers
|
|
return c.JSON(out.Status, out)
|
|
}
|
|
|
|
func apiPutServer(c echo.Context) error {
|
|
server := c.Param("server")
|
|
|
|
//check for dups
|
|
for _, s := range servers {
|
|
if s == server {
|
|
return c.JSON(errorOut(http.StatusBadRequest, "Bad request: Server already exists."))
|
|
}
|
|
}
|
|
|
|
//add servers to list
|
|
servers = append(servers, server)
|
|
|
|
//write list to disk
|
|
err := writeServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error writing servers list to disk: "+err.Error()))
|
|
}
|
|
|
|
//sync with other servers
|
|
err = syncAllServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing to other servers: "+err.Error()))
|
|
}
|
|
|
|
//renew cert
|
|
err = renew()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiDeleteServer(c echo.Context) error {
|
|
deleteServer := c.Param("server")
|
|
|
|
var newlist []string
|
|
for _, s := range servers {
|
|
if s != deleteServer {
|
|
newlist = append(newlist, s)
|
|
}
|
|
}
|
|
servers = newlist
|
|
|
|
//write list to disk
|
|
err := writeServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error writing servers list to disk: "+err.Error()))
|
|
}
|
|
|
|
//sync with other servers
|
|
err = syncAllServers()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing to other servers: "+err.Error()))
|
|
}
|
|
|
|
//renew cert
|
|
err = renew()
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error renewing: "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|
|
|
|
func apiSync(c echo.Context) error {
|
|
host := c.Param("host")
|
|
|
|
log.Println("Received sync request for host: " + host + ". From IP address: " + c.RealIP() + " Syncing...")
|
|
|
|
err := syncServersFromHost(host)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing servers from host: "+host+". "+err.Error()))
|
|
}
|
|
|
|
err = syncDomainsFromHost(host)
|
|
if err != nil {
|
|
log.Println(err.Error())
|
|
return c.JSON(errorOut(http.StatusInternalServerError, "Error syncing domains from host: "+host+". "+err.Error()))
|
|
}
|
|
|
|
return c.JSON(okOut())
|
|
}
|