Lets Encrypt certificate renewal API for server cluster and getssl.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

308 lines
7.7 KiB

//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())
}