Fixed csrf token error in cert upload ui

- Fixed csrf token error in cert upload interface
- Added system wide logger into tls cert manager
This commit is contained in:
Toby Chui 2024-07-29 12:28:21 +08:00
parent ca37bfbfa6
commit fd1439f746
6 changed files with 60 additions and 71 deletions

View File

@ -248,15 +248,19 @@ func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Reque
} }
func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) { func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
email, err := utils.PostPara(r, "set")
if err != nil {
//Return the current email to user //Return the current email to user
js, _ := json.Marshal(a.RenewerConfig.Email) js, _ := json.Marshal(a.RenewerConfig.Email)
utils.SendJSONResponse(w, string(js)) utils.SendJSONResponse(w, string(js))
} else { } else if r.Method == http.MethodPost {
email, err := utils.PostPara(r, "set")
if err != nil {
utils.SendErrorResponse(w, "invalid or empty email given")
return
}
//Check if the email is valid //Check if the email is valid
_, err := mail.ParseAddress(email) _, err = mail.ParseAddress(email)
if err != nil { if err != nil {
utils.SendErrorResponse(w, err.Error()) utils.SendErrorResponse(w, err.Error())
return return
@ -265,8 +269,11 @@ func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
//Set the new config //Set the new config
a.RenewerConfig.Email = email a.RenewerConfig.Email = email
a.saveRenewConfigToFile() a.saveRenewConfigToFile()
}
utils.SendOK(w)
} else {
http.Error(w, "405 - Method not allowed", http.StatusMethodNotAllowed)
}
} }
// Check and renew certificates. This check all the certificates in the // Check and renew certificates. This check all the certificates in the

View File

@ -123,32 +123,30 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
} }
/* WebSocket automatic proxy */ /* WebSocket automatic proxy */
if !target.DisableAutoWebSockeyProxy { requestURL := r.URL.String()
requestURL := r.URL.String() if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" {
if r.Header["Upgrade"] != nil && strings.ToLower(r.Header["Upgrade"][0]) == "websocket" { //Handle WebSocket request. Forward the custom Upgrade header and rewrite origin
//Handle WebSocket request. Forward the custom Upgrade header and rewrite origin r.Header.Set("Zr-Origin-Upgrade", "websocket")
r.Header.Set("Zr-Origin-Upgrade", "websocket") wsRedirectionEndpoint := selectedUpstream.OriginIpOrDomain
wsRedirectionEndpoint := selectedUpstream.OriginIpOrDomain if wsRedirectionEndpoint[len(wsRedirectionEndpoint)-1:] != "/" {
if wsRedirectionEndpoint[len(wsRedirectionEndpoint)-1:] != "/" { //Append / to the end of the redirection endpoint if not exists
//Append / to the end of the redirection endpoint if not exists wsRedirectionEndpoint = wsRedirectionEndpoint + "/"
wsRedirectionEndpoint = wsRedirectionEndpoint + "/"
}
if len(requestURL) > 0 && requestURL[:1] == "/" {
//Remove starting / from request URL if exists
requestURL = requestURL[1:]
}
u, _ := url.Parse("ws://" + wsRedirectionEndpoint + requestURL)
if selectedUpstream.RequireTLS {
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL)
}
h.Parent.logRequest(r, true, 101, "host-websocket", selectedUpstream.OriginIpOrDomain)
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
SkipTLSValidation: selectedUpstream.SkipCertValidations,
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
})
wspHandler.ServeHTTP(w, r)
return
} }
if len(requestURL) > 0 && requestURL[:1] == "/" {
//Remove starting / from request URL if exists
requestURL = requestURL[1:]
}
u, _ := url.Parse("ws://" + wsRedirectionEndpoint + requestURL)
if selectedUpstream.RequireTLS {
u, _ = url.Parse("wss://" + wsRedirectionEndpoint + requestURL)
}
h.Parent.logRequest(r, true, 101, "host-websocket", selectedUpstream.OriginIpOrDomain)
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
SkipTLSValidation: selectedUpstream.SkipCertValidations,
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
})
wspHandler.ServeHTTP(w, r)
return
} }
originalHostHeader := r.Host originalHostHeader := r.Host

View File

@ -137,7 +137,6 @@ type ProxyEndpoint struct {
EnablePermissionPolicyHeader bool //Enable injection of permission policy header EnablePermissionPolicyHeader bool //Enable injection of permission policy header
PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header PermissionPolicy *permissionpolicy.PermissionsPolicy //Permission policy header
DisableHopByHopHeaderRemoval bool //Do not remove hop-by-hop headers DisableHopByHopHeaderRemoval bool //Do not remove hop-by-hop headers
DisableAutoWebSockeyProxy bool //Disable auto sniffing logic for websocket upgrade
//Authentication //Authentication
RequireBasicAuth bool //Set to true to request basic auth before proxy RequireBasicAuth bool //Set to true to request basic auth before proxy

View File

@ -11,6 +11,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"imuslab.com/zoraxy/mod/info/logger"
"imuslab.com/zoraxy/mod/utils" "imuslab.com/zoraxy/mod/utils"
) )
@ -21,15 +22,16 @@ type CertCache struct {
} }
type Manager struct { type Manager struct {
CertStore string //Path where all the certs are stored CertStore string //Path where all the certs are stored
LoadedCerts []*CertCache //A list of loaded certs LoadedCerts []*CertCache //A list of loaded certs
Logger *logger.Logger //System wide logger for debug mesage
verbal bool verbal bool
} }
//go:embed localhost.pem localhost.key //go:embed localhost.pem localhost.key
var buildinCertStore embed.FS var buildinCertStore embed.FS
func NewManager(certStore string, verbal bool) (*Manager, error) { func NewManager(certStore string, verbal bool, logger *logger.Logger) (*Manager, error) {
if !utils.FileExists(certStore) { if !utils.FileExists(certStore) {
os.MkdirAll(certStore, 0775) os.MkdirAll(certStore, 0775)
} }
@ -52,6 +54,7 @@ func NewManager(certStore string, verbal bool) (*Manager, error) {
CertStore: certStore, CertStore: certStore,
LoadedCerts: []*CertCache{}, LoadedCerts: []*CertCache{},
verbal: verbal, verbal: verbal,
Logger: logger,
} }
err := thisManager.UpdateLoadedCertList() err := thisManager.UpdateLoadedCertList()
@ -78,7 +81,7 @@ func (m *Manager) UpdateLoadedCertList() error {
priKey := filepath.Join(m.CertStore, certname+".key") priKey := filepath.Join(m.CertStore, certname+".key")
certificate, err := tls.LoadX509KeyPair(pubKey, priKey) certificate, err := tls.LoadX509KeyPair(pubKey, priKey)
if err != nil { if err != nil {
log.Println("Certificate loaded failed: " + certname) m.Logger.PrintAndLog("tls-router", "Certificate load failed: "+certname, err)
continue continue
} }
@ -86,6 +89,7 @@ func (m *Manager) UpdateLoadedCertList() error {
loadedCert, err := x509.ParseCertificate(thisCert) loadedCert, err := x509.ParseCertificate(thisCert)
if err != nil { if err != nil {
//Error pasring cert, skip this byte segment //Error pasring cert, skip this byte segment
m.Logger.PrintAndLog("tls-router", "Certificate parse failed: "+certname, err)
continue continue
} }
@ -171,37 +175,10 @@ func (m *Manager) GetCert(helloInfo *tls.ClientHelloInfo) (*tls.Certificate, err
pubKey, priKey = m.GetCertByX509CNHostname(helloInfo.ServerName) pubKey, priKey = m.GetCertByX509CNHostname(helloInfo.ServerName)
} else { } else {
//Fallback to legacy method of matching certificates //Fallback to legacy method of matching certificates
/*
domainCerts, _ := m.ListCertDomains()
cloestDomainCert := matchClosestDomainCertificate(helloInfo.ServerName, domainCerts)
if cloestDomainCert != "" {
//There is a matching parent domain for this subdomain. Use this instead.
pubKey = filepath.Join(m.CertStore, cloestDomainCert+".pem")
priKey = filepath.Join(m.CertStore, cloestDomainCert+".key")
} else if m.DefaultCertExists() {
//Use default.pem and default.key
pubKey = filepath.Join(m.CertStore, "default.pem")
priKey = filepath.Join(m.CertStore, "default.key")
if m.verbal {
log.Println("No matching certificate found. Serving with default")
}
} else {
if m.verbal {
log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
}
}*/
if m.DefaultCertExists() { if m.DefaultCertExists() {
//Use default.pem and default.key //Use default.pem and default.key
pubKey = filepath.Join(m.CertStore, "default.pem") pubKey = filepath.Join(m.CertStore, "default.pem")
priKey = filepath.Join(m.CertStore, "default.key") priKey = filepath.Join(m.CertStore, "default.key")
//if m.verbal {
// log.Println("No matching certificate found. Serving with default")
//}
} else {
//if m.verbal {
// log.Println("Matching certificate not found. Serving with build-in certificate. Requesting server name: ", helloInfo.ServerName)
//}
} }
} }

View File

@ -84,7 +84,7 @@ func startupSequence() {
}) })
//Create a TLS certificate manager //Create a TLS certificate manager
tlsCertManager, err = tlscert.NewManager("./conf/certs", development) tlsCertManager, err = tlscert.NewManager("./conf/certs", development, SystemWideLogger)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -423,6 +423,8 @@
} }
if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') { if (uploadPendingPublicKey && uploadPendingPrivateKey && typeof uploadPendingPublicKey === 'object' && typeof uploadPendingPrivateKey === 'object') {
const publicKeyForm = new FormData(); const publicKeyForm = new FormData();
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey'); publicKeyForm.append('file', uploadPendingPublicKey, 'publicKey');
const privateKeyForm = new FormData(); const privateKeyForm = new FormData();
@ -430,6 +432,7 @@
const publicKeyRequest = new XMLHttpRequest(); const publicKeyRequest = new XMLHttpRequest();
publicKeyRequest.open('POST', '/api/cert/upload?ktype=pub&domain=' + domain); publicKeyRequest.open('POST', '/api/cert/upload?ktype=pub&domain=' + domain);
publicKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
publicKeyRequest.onreadystatechange = function() { publicKeyRequest.onreadystatechange = function() {
if (publicKeyRequest.readyState === XMLHttpRequest.DONE) { if (publicKeyRequest.readyState === XMLHttpRequest.DONE) {
if (publicKeyRequest.status !== 200) { if (publicKeyRequest.status !== 200) {
@ -446,6 +449,7 @@
const privateKeyRequest = new XMLHttpRequest(); const privateKeyRequest = new XMLHttpRequest();
privateKeyRequest.open('POST', '/api/cert/upload?ktype=pri&domain=' + domain); privateKeyRequest.open('POST', '/api/cert/upload?ktype=pri&domain=' + domain);
privateKeyRequest.setRequestHeader('X-CSRF-Token', csrfToken);
privateKeyRequest.onreadystatechange = function() { privateKeyRequest.onreadystatechange = function() {
if (privateKeyRequest.readyState === XMLHttpRequest.DONE) { if (privateKeyRequest.readyState === XMLHttpRequest.DONE) {
if (privateKeyRequest.status !== 200) { if (privateKeyRequest.status !== 200) {
@ -466,15 +470,11 @@
//ktype = {"pub" / "pri"} //ktype = {"pub" / "pri"}
function handleFileSelect(event, ktype="pub") { function handleFileSelect(event, ktype="pub") {
const file = event.target.files[0]; const file = event.target.files[0];
//const fileNameInput = document.getElementById('selected-file-name');
if (ktype == "pub"){ if (ktype == "pub"){
uploadPendingPublicKey = file; uploadPendingPublicKey = file;
}else if (ktype == "pri"){ }else if (ktype == "pri"){
uploadPendingPrivateKey = file; uploadPendingPrivateKey = file;
} }
//fileNameInput.value = file.name;
} }
//Check if the default keypairs exists //Check if the default keypairs exists
@ -497,6 +497,7 @@
input.addEventListener('change', () => { input.addEventListener('change', () => {
// create form data object // create form data object
const formData = new FormData(); const formData = new FormData();
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
// add selected file to form data // add selected file to form data
formData.append('file', input.files[0]); formData.append('file', input.files[0]);
@ -504,7 +505,10 @@
// send form data to server // send form data to server
fetch('/api/cert/upload?ktype=pri', { fetch('/api/cert/upload?ktype=pri', {
method: 'POST', method: 'POST',
body: formData body: formData,
headers: {
'X-CSRF-Token': csrfToken
}
}) })
.then(response => { .then(response => {
initDefaultKeypairCheck(); initDefaultKeypairCheck();
@ -531,6 +535,7 @@
function uploadPublicKey() { function uploadPublicKey() {
// create file input element // create file input element
const input = document.createElement('input'); const input = document.createElement('input');
const csrfToken = document.querySelector('meta[name="zoraxy.csrf.Token"]').getAttribute("content");
input.type = 'file'; input.type = 'file';
// add change listener to file input // add change listener to file input
@ -544,7 +549,10 @@
// send form data to server // send form data to server
fetch('/api/cert/upload?ktype=pub', { fetch('/api/cert/upload?ktype=pub', {
method: 'POST', method: 'POST',
body: formData body: formData,
headers: {
'X-CSRF-Token': csrfToken
}
}) })
.then(response => { .then(response => {
if (response.ok) { if (response.ok) {