diff --git a/README.md b/README.md index a6da9e8..6f7b476 100644 --- a/README.md +++ b/README.md @@ -83,8 +83,6 @@ Usage of zoraxy: Disable authentication for management interface -port string Management web interface listening port (default ":8000") - -rpt string - Reserved -sshlb Allow loopback web ssh connection (DANGER) -version @@ -109,39 +107,6 @@ If you already have an upstream reverse proxy server in place with permission ma *Note: For security reaons, you should only enable no-auth if you are running Zoraxy in a trusted environment or with another authentication management proxy in front.* -#### Use with ArozOS - -The [ArozOS](https://arozos.com) subservice is a built-in, permission-managed, reverse proxy server. To use Zoraxy with ArozOS, connect to your ArozOS host via SSH and use the following command to install Zoraxy: - -```bash -# cd into your ArozOS subservice folder. Sometimes it is under ~/arozos/src/subservice. -cd ~/arozos/subservices -mkdir zoraxy -cd ./zoraxy - -# Download the release binary from Github release. -wget {binary executable link from release page} - -# Set permission. Change this if required. -sudo chmod 775 -R ./ - -# Start zoraxy to see if the downloaded arch is correct. -./zoraxy - -# After unzipping, press Ctrl + C to kill it. -# Rename it to validate the ArozOS subservice binary format. -mv ./zoraxy zoraxy_linux_amd64 - -# If you are using SBCs with a different CPU arch, use the following names: -# mv ./zoraxy zoraxy_linux_arm -# mv ./zoraxy zoraxy_linux_arm64 - -# Restart ArozOS -sudo systemctl restart arozos -``` - -To start the module, go to System Settings > Modules > Subservice and enable it in the menu. You should be able to see a new module named "Zoraxy" pop up in the start menu. - ## Screenshots  diff --git a/src/api.go b/src/api.go index 1ac2eda..5fbb66f 100644 --- a/src/api.go +++ b/src/api.go @@ -119,6 +119,8 @@ func initAPIs() { authRouter.HandleFunc("/api/gan/network/name", ganManager.HandleNetworkNaming) //authRouter.HandleFunc("/api/gan/network/detail", ganManager.HandleNetworkDetails) authRouter.HandleFunc("/api/gan/network/setRange", ganManager.HandleSetRanges) + authRouter.HandleFunc("/api/gan/network/join", ganManager.HandleServerJoinNetwork) + authRouter.HandleFunc("/api/gan/network/leave", ganManager.HandleServerLeaveNetwork) authRouter.HandleFunc("/api/gan/members/list", ganManager.HandleMemberList) authRouter.HandleFunc("/api/gan/members/ip", ganManager.HandleMemberIP) authRouter.HandleFunc("/api/gan/members/name", ganManager.HandleMemberNaming) diff --git a/src/main.go b/src/main.go index 94f9988..fed0b1f 100644 --- a/src/main.go +++ b/src/main.go @@ -13,7 +13,6 @@ import ( "github.com/google/uuid" "imuslab.com/zoraxy/mod/acme" - "imuslab.com/zoraxy/mod/aroz" "imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/database" "imuslab.com/zoraxy/mod/dynamicproxy/redirection" @@ -35,6 +34,7 @@ import ( ) // General flags +var webUIPort = flag.String("port", ":8000", "Management web interface listening port") var noauth = flag.Bool("noauth", false, "Disable authentication for management interface") var showver = flag.Bool("version", false, "Show version of this server") var allowSshLoopback = flag.Bool("sshlb", false, "Allow loopback web ssh connection (DANGER)") @@ -63,7 +63,6 @@ var ( /* Handler Modules */ - handler *aroz.ArozHandler //Handle arozos managed permission system sysdb *database.Database //System database authAgent *auth.AuthAgent //Authentication agent tlsCertManager *tlscert.Manager //TLS / SSL management @@ -128,20 +127,8 @@ func ShutdownSeq() { } func main() { - //Start the aoModule pipeline (which will parse the flags as well). Pass in the module launch information - handler = aroz.HandleFlagParse(aroz.ServiceInfo{ - Name: name, - Desc: "Dynamic Reverse Proxy Server", - Group: "Network", - IconPath: "zoraxy/img/small_icon.png", - Version: version, - StartDir: "zoraxy/index.html", - SupportFW: true, - LaunchFWDir: "zoraxy/index.html", - SupportEmb: false, - InitFWSize: []int{1080, 580}, - }) - + //Parse startup flags + flag.Parse() if *showver { fmt.Println(name + " - Version " + version) os.Exit(0) @@ -166,7 +153,7 @@ func main() { startupSequence() //Initiate management interface APIs - requireAuth = !(*noauth || handler.IsUsingExternalPermissionManager()) + requireAuth = !(*noauth) initAPIs() //Start the reverse proxy server in go routine @@ -179,8 +166,8 @@ func main() { //Start the finalize sequences finalSequence() - SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + handler.Port) - err = http.ListenAndServe(handler.Port, nil) + SystemWideLogger.Println("Zoraxy started. Visit control panel at http://localhost" + *webUIPort) + err = http.ListenAndServe(*webUIPort, nil) if err != nil { log.Fatal(err) diff --git a/src/mod/ganserv/handlers.go b/src/mod/ganserv/handlers.go index c15ae19..faa0326 100644 --- a/src/mod/ganserv/handlers.go +++ b/src/mod/ganserv/handlers.go @@ -207,7 +207,7 @@ func (m *NetworkManager) HandleSetRanges(w http.ResponseWriter, r *http.Request) utils.SendOK(w) } -//Handle listing of network members. Set details=true for listing all details +// Handle listing of network members. Set details=true for listing all details func (m *NetworkManager) HandleMemberList(w http.ResponseWriter, r *http.Request) { netid, err := utils.GetPara(r, "netid") if err != nil { @@ -241,7 +241,7 @@ func (m *NetworkManager) HandleMemberList(w http.ResponseWriter, r *http.Request } } -//Handle Authorization of members +// Handle Authorization of members func (m *NetworkManager) HandleMemberAuthorization(w http.ResponseWriter, r *http.Request) { netid, err := utils.PostPara(r, "netid") if err != nil { @@ -281,7 +281,7 @@ func (m *NetworkManager) HandleMemberAuthorization(w http.ResponseWriter, r *htt } } -//Handle Delete or Add IP for a member in a network +// Handle Delete or Add IP for a member in a network func (m *NetworkManager) HandleMemberIP(w http.ResponseWriter, r *http.Request) { netid, err := utils.PostPara(r, "netid") if err != nil { @@ -356,7 +356,7 @@ func (m *NetworkManager) HandleMemberIP(w http.ResponseWriter, r *http.Request) } } -//Handle naming for members +// Handle naming for members func (m *NetworkManager) HandleMemberNaming(w http.ResponseWriter, r *http.Request) { netid, err := utils.PostPara(r, "netid") if err != nil { @@ -391,7 +391,7 @@ func (m *NetworkManager) HandleMemberNaming(w http.ResponseWriter, r *http.Reque } } -//Handle delete of a given memver +// Handle delete of a given memver func (m *NetworkManager) HandleMemberDelete(w http.ResponseWriter, r *http.Request) { netid, err := utils.PostPara(r, "netid") if err != nil { @@ -426,3 +426,79 @@ func (m *NetworkManager) HandleMemberDelete(w http.ResponseWriter, r *http.Reque utils.SendOK(w) } + +// Check if a given network id is a network hosted on this zoraxy node +func (m *NetworkManager) IsLocalGAN(networkId string) bool { + networks, err := m.listNetworkIds() + if err != nil { + return false + } + + for _, network := range networks { + if network == networkId { + return true + } + } + + return false +} + +// Handle server instant joining a given network +func (m *NetworkManager) HandleServerJoinNetwork(w http.ResponseWriter, r *http.Request) { + netid, err := utils.PostPara(r, "netid") + if err != nil { + utils.SendErrorResponse(w, "net id not set") + return + } + + //Check if the target network is a network hosted on this server + if !m.IsLocalGAN(netid) { + utils.SendErrorResponse(w, "given network is not a GAN hosted on this node") + return + } + + if m.memberExistsInNetwork(netid, m.ControllerID) { + utils.SendErrorResponse(w, "controller already inside network") + return + } + + //Join the network + err = m.joinNetwork(netid) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendOK(w) +} + +// Handle server instant leaving a given network +func (m *NetworkManager) HandleServerLeaveNetwork(w http.ResponseWriter, r *http.Request) { + netid, err := utils.PostPara(r, "netid") + if err != nil { + utils.SendErrorResponse(w, "net id not set") + return + } + + //Check if the target network is a network hosted on this server + if !m.IsLocalGAN(netid) { + utils.SendErrorResponse(w, "given network is not a GAN hosted on this node") + return + } + + //Leave the network + err = m.leaveNetwork(netid) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + //Remove it from target network if it is authorized + err = m.deleteMember(netid, m.ControllerID) + if err != nil { + utils.SendErrorResponse(w, err.Error()) + return + } + + utils.SendOK(w) +} diff --git a/src/mod/ganserv/zerotier.go b/src/mod/ganserv/zerotier.go index 3016948..3fd6cbd 100644 --- a/src/mod/ganserv/zerotier.go +++ b/src/mod/ganserv/zerotier.go @@ -117,7 +117,7 @@ type MemberInfo struct { VRev int `json:"vRev"` } -//Get the zerotier node info from local service +// Get the zerotier node info from local service func getControllerInfo(token string, apiPort int) (*NodeInfo, error) { url := "http://localhost:" + strconv.Itoa(apiPort) + "/status" @@ -187,7 +187,7 @@ func (m *NetworkManager) createNetwork() (*NetworkInfo, error) { return &networkInfo, nil } -//List network details +// List network details func (m *NetworkManager) getNetworkInfoById(networkId string) (*NetworkInfo, error) { req, err := http.NewRequest("GET", os.ExpandEnv("http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+networkId+"/"), nil) if err != nil { @@ -249,7 +249,7 @@ func (m *NetworkManager) setNetworkInfoByID(networkId string, newNetworkInfo *Ne return nil } -//List network IDs +// List network IDs func (m *NetworkManager) listNetworkIds() ([]string, error) { req, err := http.NewRequest("GET", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/", nil) if err != nil { @@ -281,7 +281,7 @@ func (m *NetworkManager) listNetworkIds() ([]string, error) { return networkIds, nil } -//wrapper for checking if a network id exists +// wrapper for checking if a network id exists func (m *NetworkManager) networkExists(networkId string) bool { networkIds, err := m.listNetworkIds() if err != nil { @@ -297,7 +297,7 @@ func (m *NetworkManager) networkExists(networkId string) bool { return false } -//delete a network +// delete a network func (m *NetworkManager) deleteNetwork(networkID string) error { url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkID + "/" client := &http.Client{} @@ -330,8 +330,8 @@ func (m *NetworkManager) deleteNetwork(networkID string) error { return nil } -//Configure network -//Example: configureNetwork(netid, "192.168.192.1", "192.168.192.254", "192.168.192.0/24") +// Configure network +// Example: configureNetwork(netid, "192.168.192.1", "192.168.192.254", "192.168.192.0/24") func (m *NetworkManager) configureNetwork(networkID string, ipRangeStart string, ipRangeEnd string, routeTarget string) error { url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + networkID + "/" data := map[string]interface{}{ @@ -545,7 +545,7 @@ func (m *NetworkManager) memberExistsInNetwork(netid string, memid string) bool return false } -//Get a network memeber info by netid and memberid +// Get a network memeber info by netid and memberid func (m *NetworkManager) getNetworkMemberInfo(netid string, memberid string) (*MemberInfo, error) { req, err := http.NewRequest("GET", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+netid+"/member/"+memberid, nil) if err != nil { @@ -573,7 +573,7 @@ func (m *NetworkManager) getNetworkMemberInfo(netid string, memberid string) (*M return thisMemeberInfo, nil } -//Set the authorization state of a member +// Set the authorization state of a member func (m *NetworkManager) AuthorizeMember(netid string, memberid string, setAuthorized bool) error { url := "http://localhost:" + strconv.Itoa(m.apiPort) + "/controller/network/" + netid + "/member/" + memberid payload := []byte(`{"authorized": true}`) @@ -600,7 +600,7 @@ func (m *NetworkManager) AuthorizeMember(netid string, memberid string, setAutho return nil } -//Delete a member from the network +// Delete a member from the network func (m *NetworkManager) deleteMember(netid string, memid string) error { req, err := http.NewRequest("DELETE", "http://localhost:"+strconv.Itoa(m.apiPort)+"/controller/network/"+netid+"/member/"+memid, nil) if err != nil { @@ -620,3 +620,45 @@ func (m *NetworkManager) deleteMember(netid string, memid string) error { return nil } + +// Make the host to join a given network +func (m *NetworkManager) joinNetwork(netid string) error { + req, err := http.NewRequest("POST", "http://localhost:"+strconv.Itoa(m.apiPort)+"/network/"+netid, nil) + if err != nil { + return err + } + req.Header.Set("X-Zt1-Auth", os.ExpandEnv(m.authToken)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode)) + } + + return nil +} + +// Make the host to leave a given network +func (m *NetworkManager) leaveNetwork(netid string) error { + req, err := http.NewRequest("DELETE", "http://localhost:"+strconv.Itoa(m.apiPort)+"/network/"+netid, nil) + if err != nil { + return err + } + req.Header.Set("X-Zt1-Auth", os.ExpandEnv(m.authToken)) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return errors.New("network error. Status code: " + strconv.Itoa(resp.StatusCode)) + } + + return nil +} diff --git a/src/start.go b/src/start.go index 5e4c85a..20a60ed 100644 --- a/src/start.go +++ b/src/start.go @@ -140,7 +140,7 @@ func startupSequence() { */ if *allowMdnsScanning { - portInt, err := strconv.Atoi(strings.Split(handler.Port, ":")[1]) + portInt, err := strconv.Atoi(strings.Split(*webUIPort, ":")[1]) if err != nil { portInt = 8000 } diff --git a/src/web/components/gan.html b/src/web/components/gan.html index 6b0bd06..833b401 100644 --- a/src/web/components/gan.html +++ b/src/web/components/gan.html @@ -24,7 +24,6 @@
To join this network using command line, type sudo zerotier-cli join
on your device terminal
Optionally you can add the network controller (ZeroTier running on the Zoraxy node) as member for cross GAN reverse proxy to bypass NAT limitations.
+ +