mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00

- Moved constants to def.go - Added acme close function (not used for now) - Added robots.txt to prevent webmin panel being scanned by search engine
183 lines
5.2 KiB
Go
183 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/rand"
|
|
"net/http"
|
|
"regexp"
|
|
"strconv"
|
|
"time"
|
|
|
|
"imuslab.com/zoraxy/mod/acme"
|
|
"imuslab.com/zoraxy/mod/dynamicproxy"
|
|
"imuslab.com/zoraxy/mod/utils"
|
|
)
|
|
|
|
/*
|
|
acme.go
|
|
|
|
This script handle special routing required for acme auto cert renew functions
|
|
*/
|
|
|
|
// Helper function to generate a random port above a specified value
|
|
func getRandomPort(minPort int) int {
|
|
return rand.Intn(65535-minPort) + minPort
|
|
}
|
|
|
|
// init the new ACME instance
|
|
func initACME() *acme.ACMEHandler {
|
|
SystemWideLogger.Println("Starting ACME handler")
|
|
rand.Seed(time.Now().UnixNano())
|
|
// Generate a random port above 30000
|
|
port := getRandomPort(30000)
|
|
|
|
// Check if the port is already in use
|
|
for acme.IsPortInUse(port) {
|
|
port = getRandomPort(30000)
|
|
}
|
|
|
|
return acme.NewACME("https://acme-v02.api.letsencrypt.org/directory", strconv.Itoa(port), sysdb, SystemWideLogger)
|
|
}
|
|
|
|
// Restart ACME handler and auto renewer
|
|
func restartACMEHandler() {
|
|
SystemWideLogger.Println("Restarting ACME handler")
|
|
//Clos the current handler and auto renewer
|
|
acmeHandler.Close()
|
|
acmeAutoRenewer.Close()
|
|
acmeDeregisterSpecialRoutingRule()
|
|
|
|
//Reinit the handler with a new random port
|
|
acmeHandler = initACME()
|
|
|
|
acmeRegisterSpecialRoutingRule()
|
|
}
|
|
|
|
// create the special routing rule for ACME
|
|
func acmeRegisterSpecialRoutingRule() {
|
|
SystemWideLogger.Println("Assigned temporary port:" + acmeHandler.Getport())
|
|
|
|
err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
|
|
ID: "acme-autorenew",
|
|
MatchRule: func(r *http.Request) bool {
|
|
found, _ := regexp.MatchString("/.well-known/acme-challenge/*", r.RequestURI)
|
|
return found
|
|
},
|
|
RoutingHandler: func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
req, err := http.NewRequest(http.MethodGet, "http://localhost:"+acmeHandler.Getport()+r.RequestURI, nil)
|
|
req.Host = r.Host
|
|
if err != nil {
|
|
fmt.Printf("client: could not create request: %s\n", err)
|
|
return
|
|
}
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
fmt.Printf("client: error making http request: %s\n", err)
|
|
return
|
|
}
|
|
|
|
resBody, err := io.ReadAll(res.Body)
|
|
defer res.Body.Close()
|
|
if err != nil {
|
|
fmt.Printf("error reading: %s\n", err)
|
|
return
|
|
}
|
|
w.Write(resBody)
|
|
},
|
|
Enabled: true,
|
|
UseSystemAccessControl: false,
|
|
})
|
|
|
|
if err != nil {
|
|
SystemWideLogger.PrintAndLog("ACME", "Unable register temp port for DNS resolver", err)
|
|
}
|
|
}
|
|
|
|
// remove the special routing rule for ACME
|
|
func acmeDeregisterSpecialRoutingRule() {
|
|
SystemWideLogger.Println("Removing ACME routing rule")
|
|
dynamicProxyRouter.RemoveRoutingRule("acme-autorenew")
|
|
}
|
|
|
|
// This function check if the renew setup is satisfied. If not, toggle them automatically
|
|
func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
|
|
isForceHttpsRedirectEnabledOriginally := false
|
|
requireRestorePort80 := false
|
|
dnsPara, _ := utils.PostBool(r, "dns")
|
|
if !dnsPara {
|
|
|
|
if dynamicProxyRouter.Option.Port == 443 {
|
|
//Check if port 80 is enabled
|
|
if !dynamicProxyRouter.Option.ListenOnPort80 {
|
|
//Enable port 80 temporarily
|
|
SystemWideLogger.PrintAndLog("ACME", "Temporarily enabling port 80 listener to handle ACME request ", nil)
|
|
dynamicProxyRouter.UpdatePort80ListenerState(true)
|
|
requireRestorePort80 = true
|
|
time.Sleep(2 * time.Second)
|
|
}
|
|
|
|
//Enable port 80 to 443 redirect
|
|
if !dynamicProxyRouter.Option.ForceHttpsRedirect {
|
|
SystemWideLogger.Println("Temporary enabling HTTP to HTTPS redirect for ACME certificate renew requests")
|
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(true)
|
|
} else {
|
|
//Set this to true, so after renew, do not turn it off
|
|
isForceHttpsRedirectEnabledOriginally = true
|
|
}
|
|
|
|
} else if dynamicProxyRouter.Option.Port == 80 {
|
|
//Go ahead
|
|
|
|
} else {
|
|
//This port do not support ACME
|
|
utils.SendErrorResponse(w, "ACME renew only support web server listening on port 80 (http) or 443 (https)")
|
|
return
|
|
}
|
|
}
|
|
|
|
//Add a 2 second delay to make sure everything is settle down
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// Pass over to the acmeHandler to deal with the communication
|
|
acmeHandler.HandleRenewCertificate(w, r)
|
|
|
|
//Update the TLS cert store buffer
|
|
tlsCertManager.UpdateLoadedCertList()
|
|
|
|
//Restore original settings
|
|
if requireRestorePort80 {
|
|
//Restore port 80 listener
|
|
SystemWideLogger.PrintAndLog("ACME", "Restoring previous port 80 listener settings", nil)
|
|
dynamicProxyRouter.UpdatePort80ListenerState(false)
|
|
}
|
|
if !isForceHttpsRedirectEnabledOriginally {
|
|
//Default is off. Turn the redirection off
|
|
SystemWideLogger.PrintAndLog("ACME", "Restoring HTTP to HTTPS redirect settings", nil)
|
|
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
|
|
}
|
|
|
|
}
|
|
|
|
// HandleACMEPreferredCA return the user preferred / default CA for new subdomain auto creation
|
|
func HandleACMEPreferredCA(w http.ResponseWriter, r *http.Request) {
|
|
ca, err := utils.PostPara(r, "set")
|
|
if err != nil {
|
|
//Return the current ca to user
|
|
prefCA := "Let's Encrypt"
|
|
sysdb.Read("acmepref", "prefca", &prefCA)
|
|
js, _ := json.Marshal(prefCA)
|
|
utils.SendJSONResponse(w, string(js))
|
|
} else {
|
|
//Check if the CA is supported
|
|
acme.IsSupportedCA(ca)
|
|
//Set the new config
|
|
sysdb.Write("acmepref", "prefca", ca)
|
|
SystemWideLogger.Println("Updating prefered ACME CA to " + ca)
|
|
utils.SendOK(w)
|
|
}
|
|
|
|
}
|