Skip to content

Commit

Permalink
Merge branch 'autorenew_experimental' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
tobychui authored Jul 9, 2023
2 parents 5193720 + 0a8a821 commit 67ba143
Show file tree
Hide file tree
Showing 32 changed files with 4,101 additions and 83 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ src/Zoraxy_*_*
src/certs/*
src/rules/*
src/README.md

docker/ContainerTester.sh
docker/ImagePublisher.sh
docker/ImagePublisher.sh
src/mod/acme/test/stackoverflow.pem
94 changes: 87 additions & 7 deletions src/acme.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
package main

import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"regexp"
"strconv"
"time"

"imuslab.com/zoraxy/mod/acme"
"imuslab.com/zoraxy/mod/dynamicproxy"
"imuslab.com/zoraxy/mod/utils"
)

/*
Expand All @@ -13,23 +21,95 @@ import (
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 {
log.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-staging-v02.api.letsencrypt.org/directory", strconv.Itoa(port))
}

// create the special routing rule for ACME
func acmeRegisterSpecialRoutingRule() {
log.Println("Assigned temporary port:" + acmeHandler.Getport())

err := dynamicProxyRouter.AddRoutingRules(&dynamicproxy.RoutingRule{
ID: "acme-autorenew",
MatchRule: func(r *http.Request) bool {
if r.RequestURI == "/.well-known/" {
return true
}

return false
found, _ := regexp.MatchString("/.well-known/*", r.RequestURI)
return found
},
RoutingHandler: func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("HELLO WORLD, THIS IS ACME REQUEST HANDLER"))

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 := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Printf("error reading: %s\n", err)
return
}
w.Write(resBody)
},
Enabled: true,
Enabled: true,
UseSystemAccessControl: false,
})

if err != nil {
log.Println("[Err] " + err.Error())
}
}

// This function check if the renew setup is satisfied. If not, toggle them automatically
func AcmeCheckAndHandleRenewCertificate(w http.ResponseWriter, r *http.Request) {
isForceHttpsRedirectEnabledOriginally := false
if dynamicProxyRouter.Option.Port == 443 {
//Enable port 80 to 443 redirect
if !dynamicProxyRouter.Option.ForceHttpsRedirect {
log.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)")
}

// Pass over to the acmeHandler to deal with the communication
acmeHandler.HandleRenewCertificate(w, r)

if dynamicProxyRouter.Option.Port == 443 {
if !isForceHttpsRedirectEnabledOriginally {
//Default is off. Turn the redirection off
log.Println("Restoring HTTP to HTTPS redirect settings")
dynamicProxyRouter.UpdateHttpToHttpsRedirectSetting(false)
}
}
}
14 changes: 14 additions & 0 deletions src/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"

"imuslab.com/zoraxy/mod/acme/acmewizard"
"imuslab.com/zoraxy/mod/auth"
"imuslab.com/zoraxy/mod/netstat"
"imuslab.com/zoraxy/mod/netutils"
Expand Down Expand Up @@ -59,6 +60,7 @@ func initAPIs() {
authRouter.HandleFunc("/api/cert/tlsRequireLatest", handleSetTlsRequireLatest)
authRouter.HandleFunc("/api/cert/upload", handleCertUpload)
authRouter.HandleFunc("/api/cert/list", handleListCertificate)
authRouter.HandleFunc("/api/cert/listdomains", handleListDomains)
authRouter.HandleFunc("/api/cert/checkDefault", handleDefaultCertCheck)
authRouter.HandleFunc("/api/cert/delete", handleCertRemove)

Expand Down Expand Up @@ -135,6 +137,7 @@ func initAPIs() {
authRouter.HandleFunc("/api/tools/ipscan", HandleIpScan)
authRouter.HandleFunc("/api/tools/traceroute", netutils.HandleTraceRoute)
authRouter.HandleFunc("/api/tools/ping", netutils.HandlePing)
authRouter.HandleFunc("/api/tools/whois", netutils.HandleWhois)
authRouter.HandleFunc("/api/tools/webssh", HandleCreateProxySession)
authRouter.HandleFunc("/api/tools/websshSupported", HandleWebSshSupportCheck)
authRouter.HandleFunc("/api/tools/wol", HandleWakeOnLan)
Expand All @@ -150,6 +153,17 @@ func initAPIs() {
//Others
http.HandleFunc("/api/info/x", HandleZoraxyInfo)

//ACME & Auto Renewer
authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)
authRouter.HandleFunc("/api/acme/autoRenew/renewPolicy", acmeAutoRenewer.HandleRenewPolicy)
authRouter.HandleFunc("/api/acme/autoRenew/renewNow", acmeAutoRenewer.HandleRenewNow)
authRouter.HandleFunc("/api/acme/wizard", acmewizard.HandleGuidedStepCheck) //ACME Wizard

//If you got APIs to add, append them here
}

Expand Down
68 changes: 68 additions & 0 deletions src/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"

"imuslab.com/zoraxy/mod/utils"
)
Expand Down Expand Up @@ -44,6 +46,7 @@ func handleListCertificate(w http.ResponseWriter, r *http.Request) {
Domain string
LastModifiedDate string
ExpireDate string
RemainingDays int
}

results := []*CertInfo{}
Expand All @@ -60,6 +63,7 @@ func handleListCertificate(w http.ResponseWriter, r *http.Request) {

certExpireTime := "Unknown"
certBtyes, err := os.ReadFile(certFilepath)
expiredIn := 0
if err != nil {
//Unable to load this file
continue
Expand All @@ -70,6 +74,11 @@ func handleListCertificate(w http.ResponseWriter, r *http.Request) {
cert, err := x509.ParseCertificate(block.Bytes)
if err == nil {
certExpireTime = cert.NotAfter.Format("2006-01-02 15:04:05")

duration := cert.NotAfter.Sub(time.Now())

// Convert the duration to days
expiredIn = int(duration.Hours() / 24)
}
}
}
Expand All @@ -78,6 +87,7 @@ func handleListCertificate(w http.ResponseWriter, r *http.Request) {
Domain: filename,
LastModifiedDate: modifiedTime,
ExpireDate: certExpireTime,
RemainingDays: expiredIn,
}

results = append(results, &thisCertInfo)
Expand All @@ -99,6 +109,64 @@ func handleListCertificate(w http.ResponseWriter, r *http.Request) {

}

// List all certificates and map all their domains to the cert filename
func handleListDomains(w http.ResponseWriter, r *http.Request) {
filenames, err := os.ReadDir("./certs/")

if err != nil {
utils.SendErrorResponse(w, err.Error())
return
}

certnameToDomainMap := map[string]string{}
for _, filename := range filenames {
if filename.IsDir() {
continue
}
certFilepath := filepath.Join("./certs/", filename.Name())

certBtyes, err := os.ReadFile(certFilepath)
if err != nil {
// Unable to load this file
log.Println("Unable to load certificate: " + certFilepath)
continue
} else {
// Cert loaded. Check its expiry time
block, _ := pem.Decode(certBtyes)
if block != nil {
cert, err := x509.ParseCertificate(block.Bytes)
if err == nil {
certname := strings.TrimSuffix(filepath.Base(certFilepath), filepath.Ext(certFilepath))
for _, dnsName := range cert.DNSNames {
certnameToDomainMap[dnsName] = certname
}
certnameToDomainMap[cert.Subject.CommonName] = certname
}
}
}
}

requireCompact, _ := utils.GetPara(r, "compact")
if requireCompact == "true" {
result := make(map[string][]string)

for key, value := range certnameToDomainMap {
if _, ok := result[value]; !ok {
result[value] = make([]string, 0)
}

result[value] = append(result[value], key)
}

js, _ := json.Marshal(result)
utils.SendJSONResponse(w, string(js))
return
}

js, _ := json.Marshal(certnameToDomainMap)
utils.SendJSONResponse(w, string(js))
}

// Handle front-end toggling TLS mode
func handleToggleTLSProxy(w http.ResponseWriter, r *http.Request) {
currentTlsSetting := false
Expand Down
6 changes: 4 additions & 2 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ go 1.16

require (
github.com/boltdb/bolt v1.3.1
github.com/go-acme/lego/v4 v4.12.1 // indirect
github.com/go-ping/ping v1.1.0
github.com/google/uuid v1.3.0
github.com/gorilla/sessions v1.2.1
github.com/gorilla/websocket v1.4.2
github.com/grandcat/zeroconf v1.0.0
github.com/likexian/whois v1.15.0 // indirect
github.com/microcosm-cc/bluemonday v1.0.24
github.com/oschwald/geoip2-golang v1.8.0
github.com/satori/go.uuid v1.2.0
golang.org/x/net v0.10.0
golang.org/x/sys v0.8.0
golang.org/x/net v0.11.0
golang.org/x/sys v0.9.0
)
Loading

0 comments on commit 67ba143

Please sign in to comment.