├── go.mod ├── LICENSE ├── go.sum ├── save.go ├── save_v6.go ├── README.md ├── chain.go ├── chain_v6.go ├── main.go ├── rules_v6.go ├── rules.go ├── raw.go ├── raw_v6.go ├── nat.go └── nat_v6.go /go.mod: -------------------------------------------------------------------------------- 1 | module iptables-api 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/abbot/go-http-auth v0.4.0 7 | github.com/gorilla/handlers v1.4.0 8 | github.com/gorilla/mux v1.7.2 9 | github.com/jeremmfr/go-iptables v0.4.3 10 | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f // indirect 11 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092 // indirect 12 | ) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Muriel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= 2 | github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= 3 | github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= 4 | github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= 5 | github.com/gorilla/mux v1.7.2 h1:zoNxOV7WjqXptQOVngLmcSQgXmgk4NMz1HibBchjl/I= 6 | github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 7 | github.com/jeremmfr/go-iptables v0.4.3 h1:cEsNNkrOVNK7wXpgAElGMs0v2ZDptfmyyOLLgRHnrc0= 8 | github.com/jeremmfr/go-iptables v0.4.3/go.mod h1:+vww2OYD9R4qNFyMjQ04RgCv9M9hLuMGm91aPy+jYt4= 9 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 10 | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo= 11 | golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 12 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 13 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= 14 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 15 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 16 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 17 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 18 | -------------------------------------------------------------------------------- /save.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | "time" 11 | 12 | auth "github.com/abbot/go-http-auth" 13 | ) 14 | 15 | // GET /save/ 16 | func saveRules(w http.ResponseWriter, r *http.Request) { 17 | if *htpasswdfile != "" { 18 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 19 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 20 | usercheck := authenticator.CheckAuth(r) 21 | if usercheck == "" { 22 | w.WriteHeader(http.StatusUnauthorized) 23 | return 24 | } 25 | } 26 | 27 | err := os.MkdirAll("/etc/iptables/", 0755) 28 | if err != nil { 29 | http.Error(w, err.Error(), 500) 30 | return 31 | } 32 | stdout, err := exec.Command("iptables-save").Output() 33 | if err != nil { 34 | http.Error(w, err.Error(), 500) 35 | return 36 | } 37 | err = ioutil.WriteFile("/etc/iptables/rules.v4", stdout, 0644) 38 | if err != nil { 39 | http.Error(w, err.Error(), 500) 40 | return 41 | } 42 | err = os.MkdirAll(*savePath, 0755) 43 | if err != nil { 44 | http.Error(w, err.Error(), 500) 45 | return 46 | } 47 | 48 | currentTime := time.Now().Local() 49 | dstFile := []string{*savePath, "rules.v4.", currentTime.Format("2006-01-02"), ".", currentTime.Format("15-04-05")} 50 | cmd := exec.Command("cp", "/etc/iptables/rules.v4", strings.Join(dstFile, "")) 51 | err = cmd.Run() 52 | if err != nil { 53 | http.Error(w, err.Error(), 500) 54 | return 55 | } 56 | fmt.Fprintln(w, strings.Join(dstFile, "")) 57 | } 58 | 59 | // GET /restore/{file} 60 | func restoreRules(w http.ResponseWriter, r *http.Request) { 61 | if *htpasswdfile != "" { 62 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 63 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 64 | usercheck := authenticator.CheckAuth(r) 65 | if usercheck == "" { 66 | w.WriteHeader(http.StatusUnauthorized) 67 | return 68 | } 69 | } 70 | err := exec.Command("iptables-restore", r.URL.Query().Get("file")).Run() 71 | if err != nil { 72 | http.Error(w, err.Error(), 500) 73 | return 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /save_v6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | "time" 11 | 12 | auth "github.com/abbot/go-http-auth" 13 | ) 14 | 15 | // GET /save_v6/ 16 | func saveRulesV6(w http.ResponseWriter, r *http.Request) { 17 | if *htpasswdfile != "" { 18 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 19 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 20 | usercheck := authenticator.CheckAuth(r) 21 | if usercheck == "" { 22 | w.WriteHeader(http.StatusUnauthorized) 23 | return 24 | } 25 | } 26 | 27 | err := os.MkdirAll("/etc/iptables/", 0755) 28 | if err != nil { 29 | http.Error(w, err.Error(), 500) 30 | return 31 | } 32 | stdout, err := exec.Command("ip6tables-save").Output() 33 | if err != nil { 34 | http.Error(w, err.Error(), 500) 35 | return 36 | } 37 | err = ioutil.WriteFile("/etc/iptables/rules.v6", stdout, 0644) 38 | if err != nil { 39 | http.Error(w, err.Error(), 500) 40 | return 41 | } 42 | err = os.MkdirAll(*savePath, 0755) 43 | if err != nil { 44 | http.Error(w, err.Error(), 500) 45 | return 46 | } 47 | currentTime := time.Now().Local() 48 | dstFile := []string{*savePath, "rules.v6.", currentTime.Format("2006-01-02"), ".", currentTime.Format("15-04-05")} 49 | cmd := exec.Command("cp", "/etc/iptables/rules.v6", strings.Join(dstFile, "")) 50 | err = cmd.Run() 51 | if err != nil { 52 | http.Error(w, err.Error(), 500) 53 | return 54 | } 55 | fmt.Fprintln(w, strings.Join(dstFile, "")) 56 | } 57 | 58 | // GET /restore_v6/{file} 59 | func restoreRulesV6(w http.ResponseWriter, r *http.Request) { 60 | if *htpasswdfile != "" { 61 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 62 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 63 | usercheck := authenticator.CheckAuth(r) 64 | if usercheck == "" { 65 | w.WriteHeader(http.StatusUnauthorized) 66 | return 67 | } 68 | } 69 | err := exec.Command("ip6tables-restore", r.URL.Query().Get("file")).Run() 70 | if err != nil { 71 | http.Error(w, err.Error(), 500) 72 | return 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iptables-api 2 | 3 | 4 | See [![new_repository](https://img.shields.io/badge/github-jeremmfr%2Fiptables--api-critical)](https://github.com/jeremmfr/iptables-api) for next commits/releases 5 | 6 | # 7 | [![GoDoc](https://godoc.org/github.com/jeremmfr/iptables-api?status.svg)](https://godoc.org/github.com/jeremmfr/iptables-api) [![Go Report Card](https://goreportcard.com/badge/github.com/jeremmfr/iptables-api)](https://goreportcard.com/report/github.com/jeremmfr/iptables-api) 8 | 9 | Create API REST for iptables command 10 | 11 | Compile: 12 | -------- 13 | export GO111MODULE=on 14 | go build -o iptables-api 15 | 16 | Run: 17 | ---- 18 | ./iptables-api -h 19 | Usage of /root/iptables-api: 20 | -cert string 21 | file of certificat for https 22 | -htpasswd string 23 | htpasswd file for login:password 24 | -https 25 | https = true or false 26 | -ip string 27 | listen on IP (default "127.0.0.1") 28 | -key string 29 | file of key for https 30 | -log string 31 | file for access log (default "/var/log/iptables-api.access.log") 32 | -port string 33 | listen on port (default "8080") 34 | -save_path string 35 | path for backups => /save (default "var/backups/iptables-api/") 36 | 37 | ./iptables-api -https -ip=192.168.0.1 -port=8443 -log=/var/log/iptables-api.access.log -cert=cert.pem -key=key.pem -htpasswd=/root/.htpasswd 38 | 39 | API List : 40 | --------- 41 | 42 | **Rules:** 43 | 44 | Test,Add,Del iptables rule in table filter with the parameters 45 | 46 | GET/PUT/DELETE /rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&state=XXXX&fragment=true&icmptype=XXXX&log-prefix=XXXXX 47 | 48 | with for source and destination _ instead / : 10.0.0.0_8 or range 10.0.0.0-10.255.0.0_32 49 | log-prefix only if action = LOG 50 | 51 | **Nat:** 52 | 53 | Test,Add,Del iptables rule in table nat with the parameters 54 | 55 | GET/PUT/DELETE /nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00&except=true 56 | 57 | with for source and destination _ instead / : 10.0.0.0_8 58 | 59 | **Raw:** 60 | 61 | Test,Add,Del iptables rule in table raw with the parameters 62 | 63 | GET/PUT/DELETE /raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true&log-prefix=XXXXX 64 | 65 | with for source and destination _ instead / : 10.0.0.0_8 or range 10.0.0.0-10.255.0.0_32 66 | log-prefix only if action = LOG 67 | 68 | **Chain:** 69 | 70 | Test,Add,Del chain with the parameters 71 | 72 | GET/PUT/DELETE /chain/{table}/{name}/ 73 | 74 | Rename chain with the parameters 75 | 76 | PUT /mvchain/{table}/{oldname}/{newname}/ 77 | 78 | **Save:** 79 | 80 | iptables-save > /etc/iptables/rules.v4 && cp /etc/iptables/rules.v4 /var/backups/iptables-api/rules.v4.2006-01-02.15-04-05 81 | 82 | GET /save/ 83 | 84 | **Restore:** 85 | 86 | iptables-restore $file 87 | 88 | PUT /restore/{file} 89 | -------------------------------------------------------------------------------- /chain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | auth "github.com/abbot/go-http-auth" 8 | "github.com/gorilla/mux" 9 | "github.com/jeremmfr/go-iptables/iptables" 10 | ) 11 | 12 | // PUT /chain/{table}/{name}/ 13 | func addChain(w http.ResponseWriter, r *http.Request) { 14 | if *htpasswdfile != "" { 15 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 16 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 17 | usercheck := authenticator.CheckAuth(r) 18 | if usercheck == "" { 19 | w.WriteHeader(http.StatusUnauthorized) 20 | return 21 | } 22 | } 23 | vars := mux.Vars(r) 24 | 25 | ipt, err := iptables.New() 26 | if err != nil { 27 | http.Error(w, err.Error(), 500) 28 | return 29 | } 30 | respErr = ipt.NewChain(vars["table"], vars["name"]) 31 | if respErr != nil { 32 | w.WriteHeader(http.StatusBadRequest) 33 | fmt.Fprintln(w, respErr) 34 | } 35 | } 36 | 37 | // DELETE /chain/{table}/{name}/ 38 | func delChain(w http.ResponseWriter, r *http.Request) { 39 | if *htpasswdfile != "" { 40 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 41 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 42 | usercheck := authenticator.CheckAuth(r) 43 | if usercheck == "" { 44 | w.WriteHeader(http.StatusUnauthorized) 45 | return 46 | } 47 | } 48 | vars := mux.Vars(r) 49 | 50 | ipt, err := iptables.New() 51 | if err != nil { 52 | http.Error(w, err.Error(), 500) 53 | return 54 | } 55 | // Clear chain before delete 56 | respErr = ipt.ClearChain(vars["table"], vars["name"]) 57 | if respErr != nil { 58 | w.WriteHeader(http.StatusBadRequest) 59 | fmt.Fprintln(w, respErr) 60 | } 61 | // Delete chain 62 | respErr = ipt.DeleteChain(vars["table"], vars["name"]) 63 | if respErr != nil { 64 | w.WriteHeader(http.StatusBadRequest) 65 | fmt.Fprintln(w, respErr) 66 | } 67 | } 68 | 69 | // GET /chain/{table}/{name}/ 70 | func listChain(w http.ResponseWriter, r *http.Request) { 71 | if *htpasswdfile != "" { 72 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 73 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 74 | usercheck := authenticator.CheckAuth(r) 75 | if usercheck == "" { 76 | w.WriteHeader(http.StatusUnauthorized) 77 | return 78 | } 79 | } 80 | vars := mux.Vars(r) 81 | 82 | ipt, err := iptables.New() 83 | if err != nil { 84 | http.Error(w, err.Error(), 500) 85 | return 86 | } 87 | respStr, respErr := ipt.List(vars["table"], vars["name"]) 88 | if respErr != nil { 89 | w.WriteHeader(http.StatusBadRequest) 90 | fmt.Fprintln(w, respErr) 91 | } 92 | for i := 0; i < len(respStr); i++ { 93 | fmt.Fprintln(w, respStr[i]) 94 | } 95 | } 96 | 97 | // PUT /mvchain/{table}/{oldname}/{newname}/ 98 | func renameChain(w http.ResponseWriter, r *http.Request) { 99 | if *htpasswdfile != "" { 100 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 101 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 102 | usercheck := authenticator.CheckAuth(r) 103 | if usercheck == "" { 104 | w.WriteHeader(http.StatusUnauthorized) 105 | return 106 | } 107 | } 108 | vars := mux.Vars(r) 109 | 110 | ipt, err := iptables.New() 111 | if err != nil { 112 | http.Error(w, err.Error(), 500) 113 | return 114 | } 115 | respErr = ipt.RenameChain(vars["table"], vars["oldname"], vars["newname"]) 116 | if respErr != nil { 117 | w.WriteHeader(http.StatusBadRequest) 118 | fmt.Fprintln(w, respErr) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /chain_v6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | 7 | auth "github.com/abbot/go-http-auth" 8 | "github.com/gorilla/mux" 9 | "github.com/jeremmfr/go-iptables/iptables" 10 | ) 11 | 12 | // PUT /chain_v6/{table}/{name}/ 13 | func addChainV6(w http.ResponseWriter, r *http.Request) { 14 | if *htpasswdfile != "" { 15 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 16 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 17 | usercheck := authenticator.CheckAuth(r) 18 | if usercheck == "" { 19 | w.WriteHeader(http.StatusUnauthorized) 20 | return 21 | } 22 | } 23 | vars := mux.Vars(r) 24 | 25 | ipt, err := iptables.NewWithProtocol(v6) 26 | if err != nil { 27 | http.Error(w, err.Error(), 500) 28 | return 29 | } 30 | respErr = ipt.NewChain(vars["table"], vars["name"]) 31 | if respErr != nil { 32 | w.WriteHeader(http.StatusBadRequest) 33 | fmt.Fprintln(w, respErr) 34 | } 35 | } 36 | 37 | // DELETE /chain_v6/{table}/{name}/ 38 | func delChainV6(w http.ResponseWriter, r *http.Request) { 39 | if *htpasswdfile != "" { 40 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 41 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 42 | usercheck := authenticator.CheckAuth(r) 43 | if usercheck == "" { 44 | w.WriteHeader(http.StatusUnauthorized) 45 | return 46 | } 47 | } 48 | vars := mux.Vars(r) 49 | 50 | ipt, err := iptables.NewWithProtocol(v6) 51 | if err != nil { 52 | http.Error(w, err.Error(), 500) 53 | return 54 | } 55 | 56 | // Clear chain before delete 57 | respErr = ipt.ClearChain(vars["table"], vars["name"]) 58 | if respErr != nil { 59 | w.WriteHeader(http.StatusBadRequest) 60 | fmt.Fprintln(w, respErr) 61 | } 62 | // Delete chain 63 | respErr = ipt.DeleteChain(vars["table"], vars["name"]) 64 | if respErr != nil { 65 | w.WriteHeader(http.StatusBadRequest) 66 | fmt.Fprintln(w, respErr) 67 | } 68 | } 69 | 70 | // GET /chain_v6/{table}/{name}/ 71 | func listChainV6(w http.ResponseWriter, r *http.Request) { 72 | if *htpasswdfile != "" { 73 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 74 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 75 | usercheck := authenticator.CheckAuth(r) 76 | if usercheck == "" { 77 | w.WriteHeader(http.StatusUnauthorized) 78 | return 79 | } 80 | } 81 | vars := mux.Vars(r) 82 | 83 | ipt, err := iptables.NewWithProtocol(v6) 84 | if err != nil { 85 | http.Error(w, err.Error(), 500) 86 | return 87 | } 88 | respStr, respErr := ipt.List(vars["table"], vars["name"]) 89 | if respErr != nil { 90 | w.WriteHeader(http.StatusBadRequest) 91 | fmt.Fprintln(w, respErr) 92 | } 93 | for i := 0; i < len(respStr); i++ { 94 | fmt.Fprintln(w, respStr[i]) 95 | } 96 | } 97 | 98 | // PUT /mvchain_v6/{table}/{oldname}/{newname}/ 99 | func renameChainV6(w http.ResponseWriter, r *http.Request) { 100 | if *htpasswdfile != "" { 101 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 102 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 103 | usercheck := authenticator.CheckAuth(r) 104 | if usercheck == "" { 105 | w.WriteHeader(http.StatusUnauthorized) 106 | return 107 | } 108 | } 109 | vars := mux.Vars(r) 110 | 111 | ipt, err := iptables.NewWithProtocol(v6) 112 | if err != nil { 113 | http.Error(w, err.Error(), 500) 114 | return 115 | } 116 | respErr = ipt.RenameChain(vars["table"], vars["oldname"], vars["newname"]) 117 | if respErr != nil { 118 | w.WriteHeader(http.StatusBadRequest) 119 | fmt.Fprintln(w, respErr) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "net/http" 7 | "os" 8 | "strings" 9 | 10 | "github.com/gorilla/handlers" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | const ( 16 | // 17 | v6 iptables.Protocol = iota + 1 18 | dnatAct string = "dnat" 19 | snatAct string = "snat" 20 | logAct string = "LOG" 21 | trueStr string = "true" 22 | tcpStr string = "tcp" 23 | SYNStr string = "SYN" 24 | defaultFlagsMask string = "FIN,SYN,RST,ACK" 25 | defaultFlagsMask2 string = "SYN,RST,ACK,FIN" 26 | ) 27 | 28 | var ( 29 | respErr error 30 | htpasswdfile *string 31 | savePath *string 32 | ) 33 | 34 | func main() { 35 | listenIP := flag.String("ip", "127.0.0.1", "listen on IP") 36 | listenPort := flag.String("port", "8080", "listen on port") 37 | https := flag.Bool("https", false, "https = true or false") 38 | cert := flag.String("cert", "", "file of certificat for https") 39 | key := flag.String("key", "", "file of key for https") 40 | accessLogFile := flag.String("log", "/var/log/iptables-api.access.log", "file for access log") 41 | htpasswdfile = flag.String("htpasswd", "", "htpasswd file for login:password") 42 | savePath = flag.String("savepath", "/var/backups/iptables-api/", "path for backups file on /save") 43 | 44 | flag.Parse() 45 | 46 | // accesslog file open 47 | accessLog, err := os.OpenFile(*accessLogFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) 48 | if err != nil { 49 | log.Fatalf("Failed to open access log: %s", err) 50 | } 51 | 52 | // create router 53 | router := mux.NewRouter().StrictSlash(true) 54 | 55 | // ipv4 api 56 | router.HandleFunc("/rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", addRules).Methods("PUT") 57 | router.HandleFunc("/rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", delRules).Methods("DELETE") 58 | router.HandleFunc("/rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", checkRules).Methods("GET") 59 | router.HandleFunc("/raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", addRaw).Methods("PUT") 60 | router.HandleFunc("/raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", delRaw).Methods("DELETE") 61 | router.HandleFunc("/raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", checkRaw).Methods("GET") 62 | router.HandleFunc("/nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", addNat).Methods("PUT") 63 | router.HandleFunc("/nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", delNat).Methods("DELETE") 64 | router.HandleFunc("/nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", checkNat).Methods("GET") 65 | router.HandleFunc("/chain/{table}/{name}/", addChain).Methods("PUT") 66 | router.HandleFunc("/chain/{table}/{name}/", delChain).Methods("DELETE") 67 | router.HandleFunc("/chain/{table}/{name}/", listChain).Methods("GET") 68 | router.HandleFunc("/mvchain/{table}/{oldname}/{newname}/", renameChain).Methods("PUT") 69 | router.HandleFunc("/save/", saveRules).Methods("GET") 70 | router.HandleFunc("/restore/", restoreRules).Methods("PUT") 71 | 72 | // ipv6 api 73 | router.HandleFunc("/rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", addRulesV6).Methods("PUT") 74 | router.HandleFunc("/rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", delRulesV6).Methods("DELETE") 75 | router.HandleFunc("/rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", checkRulesV6).Methods("GET") 76 | router.HandleFunc("/raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", addRawV6).Methods("PUT") 77 | router.HandleFunc("/raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", delRawV6).Methods("DELETE") 78 | router.HandleFunc("/raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/", checkRawV6).Methods("GET") 79 | router.HandleFunc("/nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", addNatV6).Methods("PUT") 80 | router.HandleFunc("/nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", delNatV6).Methods("DELETE") 81 | router.HandleFunc("/nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/", checkNatV6).Methods("GET") 82 | router.HandleFunc("/chain_v6/{table}/{name}/", addChainV6).Methods("PUT") 83 | router.HandleFunc("/chain_v6/{table}/{name}/", delChainV6).Methods("DELETE") 84 | router.HandleFunc("/chain_v6/{table}/{name}/", listChainV6).Methods("GET") 85 | router.HandleFunc("/mvchain_v6/{table}/{oldname}/{newname}/", renameChainV6).Methods("PUT") 86 | router.HandleFunc("/save_v6/", saveRulesV6).Methods("GET") 87 | router.HandleFunc("/restore_v6/", restoreRulesV6).Methods("PUT") 88 | 89 | loggedRouter := handlers.CombinedLoggingHandler(accessLog, router) 90 | 91 | if *https { 92 | if (*cert == "") || (*key == "") { 93 | log.Fatalf("HTTPS true but no cert and key defined") 94 | } else { 95 | log.Fatal(http.ListenAndServeTLS(strings.Join([]string{*listenIP, ":", *listenPort}, ""), *cert, *key, loggedRouter)) 96 | } 97 | } else { 98 | log.Fatal(http.ListenAndServe(strings.Join([]string{*listenIP, ":", *listenPort}, ""), loggedRouter)) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /rules_v6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func ruleGenerateV6(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | var specEnd []string 18 | 19 | if r.URL.Query().Get("sports") != "" { 20 | specEnd = append(specEnd, "-m", "multiport", "--sports", r.URL.Query().Get("sports")) 21 | } 22 | if r.URL.Query().Get("dports") != "" { 23 | specEnd = append(specEnd, "-m", "multiport", "--dports", r.URL.Query().Get("dports")) 24 | } 25 | if r.URL.Query().Get("state") != "" { 26 | specEnd = append(specEnd, "-m", "state", "--state", r.URL.Query().Get("state")) 27 | } 28 | if r.URL.Query().Get("icmptype") != "" { 29 | specEnd = append(specEnd, "--icmpv6-type", r.URL.Query().Get("icmptype")) 30 | } 31 | if vars["iface_in"] != "*" { 32 | specEnd = append(specEnd, "-i", vars["iface_in"]) 33 | } 34 | if vars["iface_out"] != "*" { 35 | specEnd = append(specEnd, "-o", vars["iface_out"]) 36 | } 37 | srcRange := strings.Contains(vars["source"], "-") 38 | dstRange := strings.Contains(vars["destination"], "-") 39 | ruleSpecs := []string{"-p", vars["proto"]} 40 | if srcRange { 41 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_128", "", -1)) 42 | } else { 43 | ruleSpecs = append(ruleSpecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 44 | } 45 | if dstRange { 46 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_128", "", -1)) 47 | } else { 48 | ruleSpecs = append(ruleSpecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 49 | } 50 | ruleSpecs = append(ruleSpecs, "-j", vars["action"]) 51 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 52 | ruleSpecs = append(ruleSpecs, "--log-prefix", r.URL.Query().Get("log-prefix")) 53 | } 54 | ruleSpecs = append(ruleSpecs, specEnd...) 55 | return ruleSpecs 56 | } 57 | 58 | func checkPosRulesV6(r *http.Request) ([]string, error) { 59 | vars := mux.Vars(r) 60 | var linenumber []string 61 | 62 | line := []string{vars["action"], vars["proto"]} 63 | line = append(line, vars["iface_in"], vars["iface_out"]) 64 | 65 | srcRange := strings.Contains(vars["source"], "-") 66 | if srcRange { 67 | line = append(line, "::/0") 68 | } else { 69 | source128 := strings.Contains(vars["source"], "_128") 70 | if source128 { 71 | line = append(line, strings.Replace(vars["source"], "_128", "", -1)) 72 | } else { 73 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 74 | } 75 | } 76 | 77 | dstRange := strings.Contains(vars["destination"], "-") 78 | if dstRange { 79 | line = append(line, "::/0") 80 | } else { 81 | destination128 := strings.Contains(vars["destination"], "_128") 82 | if destination128 { 83 | line = append(line, strings.Replace(vars["destination"], "_128", "", -1)) 84 | } else { 85 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 86 | } 87 | } 88 | if srcRange { 89 | line = append(line, "source", "IP", "range", strings.Replace(vars["source"], "_128", "", -1)) 90 | } 91 | if dstRange { 92 | line = append(line, "destination", "IP", "range", strings.Replace(vars["destination"], "_128", "", -1)) 93 | } 94 | if r.URL.Query().Get("sports") != "" { 95 | line = append(line, "multiport", "sports", r.URL.Query().Get("sports")) 96 | } 97 | if r.URL.Query().Get("dports") != "" { 98 | line = append(line, "multiport", "dports", r.URL.Query().Get("dports")) 99 | } 100 | if r.URL.Query().Get("icmptype") != "" { 101 | line = append(line, "ipv6-icmptype", r.URL.Query().Get("icmptype")) 102 | } 103 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 104 | line = append(line, "LOG", "flags", "0", "level", "4", "prefix", strings.Join([]string{"\"", r.URL.Query().Get("log-prefix"), "\""}, "")) 105 | } 106 | ipt, err := iptables.NewWithProtocol(v6) 107 | if err != nil { 108 | return nil, err 109 | } 110 | args := []string{"-t", "filter", "-vnL", vars["chain"], "--line-numbers"} 111 | if ipt.HasWait { 112 | args = append(args, "--wait") 113 | } 114 | rules, err := ipt.ExecuteList(args) 115 | if err != nil { 116 | return nil, err 117 | } 118 | for i := 0; i < len(rules); i++ { 119 | rulesSlice := strings.Fields(rules[i]) 120 | rulesSliceNoVerb := rulesSlice[3:] 121 | if reflect.DeepEqual(line, rulesSliceNoVerb) { 122 | linenumber = append(linenumber, rulesSlice[0]) 123 | } 124 | } 125 | return linenumber, nil 126 | 127 | } 128 | 129 | // PUT /rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 130 | func addRulesV6(w http.ResponseWriter, r *http.Request) { 131 | if *htpasswdfile != "" { 132 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 133 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 134 | usercheck := authenticator.CheckAuth(r) 135 | if usercheck == "" { 136 | w.WriteHeader(http.StatusUnauthorized) 137 | return 138 | } 139 | } 140 | rulespecs := ruleGenerateV6(r) 141 | ipt, err := iptables.NewWithProtocol(v6) 142 | if err != nil { 143 | http.Error(w, err.Error(), 500) 144 | return 145 | } 146 | if ipt.HasWait { 147 | rulespecs = append(rulespecs, "--wait") 148 | } 149 | vars := mux.Vars(r) 150 | if r.URL.Query().Get("position") != "" { 151 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 152 | if err != nil { 153 | http.Error(w, err.Error(), 500) 154 | return 155 | } 156 | respErr = ipt.Insert("filter", vars["chain"], position, rulespecs...) 157 | } else { 158 | respErr = ipt.Append("filter", vars["chain"], rulespecs...) 159 | } 160 | if respErr != nil { 161 | w.WriteHeader(http.StatusBadRequest) 162 | fmt.Fprintln(w, respErr) 163 | } 164 | } 165 | 166 | // DELETE /rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 167 | func delRulesV6(w http.ResponseWriter, r *http.Request) { 168 | if *htpasswdfile != "" { 169 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 170 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 171 | usercheck := authenticator.CheckAuth(r) 172 | if usercheck == "" { 173 | w.WriteHeader(http.StatusUnauthorized) 174 | return 175 | } 176 | } 177 | rulespecs := ruleGenerateV6(r) 178 | ipt, err := iptables.NewWithProtocol(v6) 179 | if err != nil { 180 | http.Error(w, err.Error(), 500) 181 | return 182 | } 183 | if ipt.HasWait { 184 | rulespecs = append(rulespecs, "--wait") 185 | } 186 | vars := mux.Vars(r) 187 | respErr = ipt.Delete("filter", vars["chain"], rulespecs...) 188 | if respErr != nil { 189 | w.WriteHeader(http.StatusBadRequest) 190 | fmt.Fprintln(w, respErr) 191 | } 192 | } 193 | 194 | // GET /rules_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 195 | func checkRulesV6(w http.ResponseWriter, r *http.Request) { 196 | if *htpasswdfile != "" { 197 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 198 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 199 | usercheck := authenticator.CheckAuth(r) 200 | if usercheck == "" { 201 | w.WriteHeader(http.StatusUnauthorized) 202 | return 203 | } 204 | } 205 | rulespecs := ruleGenerateV6(r) 206 | ipt, err := iptables.NewWithProtocol(v6) 207 | if err != nil { 208 | http.Error(w, err.Error(), 500) 209 | return 210 | } 211 | if ipt.HasWait { 212 | rulespecs = append(rulespecs, "--wait") 213 | } 214 | if r.URL.Query().Get("position") != "" { 215 | posRules, err := checkPosRulesV6(r) 216 | if err != nil { 217 | http.Error(w, err.Error(), 500) 218 | return 219 | } 220 | switch { 221 | case len(posRules) == 0: 222 | w.WriteHeader(http.StatusNotFound) 223 | return 224 | case len(posRules) != 1: 225 | w.WriteHeader(http.StatusConflict) 226 | return 227 | case posRules[0] == r.URL.Query().Get("position"): 228 | return 229 | default: 230 | w.WriteHeader(http.StatusNotFound) 231 | return 232 | } 233 | } else { 234 | vars := mux.Vars(r) 235 | respStr, respErr := ipt.Exists("filter", vars["chain"], rulespecs...) 236 | if respErr != nil { 237 | w.WriteHeader(http.StatusBadRequest) 238 | fmt.Fprintln(w, respErr) 239 | return 240 | } 241 | if !respStr { 242 | w.WriteHeader(http.StatusNotFound) 243 | return 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /rules.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func ruleGenerate(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | var specEnd []string 18 | 19 | if r.URL.Query().Get("sports") != "" { 20 | specEnd = append(specEnd, "-m", "multiport", "--sports", r.URL.Query().Get("sports")) 21 | } 22 | if r.URL.Query().Get("dports") != "" { 23 | specEnd = append(specEnd, "-m", "multiport", "--dports", r.URL.Query().Get("dports")) 24 | } 25 | if r.URL.Query().Get("state") != "" { 26 | specEnd = append(specEnd, "-m", "state", "--state", r.URL.Query().Get("state")) 27 | } 28 | if r.URL.Query().Get("fragment") != "" { 29 | specEnd = append(specEnd, "-f") 30 | } 31 | if r.URL.Query().Get("icmptype") != "" { 32 | specEnd = append(specEnd, "--icmp-type", r.URL.Query().Get("icmptype")) 33 | } 34 | if vars["iface_in"] != "*" { 35 | specEnd = append(specEnd, "-i", vars["iface_in"]) 36 | } 37 | if vars["iface_out"] != "*" { 38 | specEnd = append(specEnd, "-o", vars["iface_out"]) 39 | } 40 | srcRange := strings.Contains(vars["source"], "-") 41 | dstRange := strings.Contains(vars["destination"], "-") 42 | ruleSpecs := []string{"-p", vars["proto"]} 43 | if srcRange { 44 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_32", "", -1)) 45 | } else { 46 | ruleSpecs = append(ruleSpecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 47 | } 48 | if dstRange { 49 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_32", "", -1)) 50 | } else { 51 | ruleSpecs = append(ruleSpecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 52 | } 53 | ruleSpecs = append(ruleSpecs, "-j", vars["action"]) 54 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 55 | ruleSpecs = append(ruleSpecs, "--log-prefix", r.URL.Query().Get("log-prefix")) 56 | } 57 | ruleSpecs = append(ruleSpecs, specEnd...) 58 | return ruleSpecs 59 | } 60 | 61 | func checkPosRules(r *http.Request) ([]string, error) { 62 | vars := mux.Vars(r) 63 | var linenumber []string 64 | 65 | line := []string{vars["action"], vars["proto"]} 66 | if r.URL.Query().Get("fragment") != "" { 67 | line = append(line, "-f") 68 | } else { 69 | line = append(line, "--") 70 | } 71 | line = append(line, vars["iface_in"], vars["iface_out"]) 72 | 73 | srcRange := strings.Contains(vars["source"], "-") 74 | if srcRange { 75 | line = append(line, "0.0.0.0/0") 76 | } else { 77 | source32 := strings.Contains(vars["source"], "_32") 78 | if source32 { 79 | line = append(line, strings.Replace(vars["source"], "_32", "", -1)) 80 | } else { 81 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 82 | } 83 | } 84 | 85 | dstRange := strings.Contains(vars["destination"], "-") 86 | if dstRange { 87 | line = append(line, "0.0.0.0/0") 88 | } else { 89 | destination32 := strings.Contains(vars["destination"], "_32") 90 | if destination32 { 91 | line = append(line, strings.Replace(vars["destination"], "_32", "", -1)) 92 | } else { 93 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 94 | } 95 | } 96 | if srcRange { 97 | line = append(line, "source", "IP", "range", strings.Replace(vars["source"], "_32", "", -1)) 98 | } 99 | if dstRange { 100 | line = append(line, "destination", "IP", "range", strings.Replace(vars["destination"], "_32", "", -1)) 101 | } 102 | if r.URL.Query().Get("sports") != "" { 103 | line = append(line, "multiport", "sports", r.URL.Query().Get("sports")) 104 | } 105 | if r.URL.Query().Get("dports") != "" { 106 | line = append(line, "multiport", "dports", r.URL.Query().Get("dports")) 107 | } 108 | if r.URL.Query().Get("icmptype") != "" { 109 | line = append(line, "icmptype", r.URL.Query().Get("icmptype")) 110 | } 111 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 112 | line = append(line, "LOG", "flags", "0", "level", "4", "prefix", strings.Join([]string{"\"", r.URL.Query().Get("log-prefix"), "\""}, "")) 113 | } 114 | if vars["action"] == "REJECT" { 115 | line = append(line, "reject-with", "icmp-port-unreachable") 116 | } 117 | ipt, err := iptables.New() 118 | if err != nil { 119 | return nil, err 120 | } 121 | args := []string{"-t", "filter", "-vnL", vars["chain"], "--line-numbers"} 122 | if ipt.HasWait { 123 | args = append(args, "--wait") 124 | } 125 | rules, err := ipt.ExecuteList(args) 126 | if err != nil { 127 | return nil, err 128 | } 129 | for i := 0; i < len(rules); i++ { 130 | rulesSlice := strings.Fields(rules[i]) 131 | rulesSliceNoVerb := rulesSlice[3:] 132 | if reflect.DeepEqual(line, rulesSliceNoVerb) { 133 | linenumber = append(linenumber, rulesSlice[0]) 134 | } 135 | } 136 | return linenumber, nil 137 | 138 | } 139 | 140 | // PUT /rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 141 | func addRules(w http.ResponseWriter, r *http.Request) { 142 | if *htpasswdfile != "" { 143 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 144 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 145 | usercheck := authenticator.CheckAuth(r) 146 | if usercheck == "" { 147 | w.WriteHeader(http.StatusUnauthorized) 148 | return 149 | } 150 | } 151 | rulespecs := ruleGenerate(r) 152 | ipt, err := iptables.New() 153 | if err != nil { 154 | http.Error(w, err.Error(), 500) 155 | return 156 | } 157 | if ipt.HasWait { 158 | rulespecs = append(rulespecs, "--wait") 159 | } 160 | vars := mux.Vars(r) 161 | if r.URL.Query().Get("position") != "" { 162 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 163 | if err != nil { 164 | http.Error(w, err.Error(), 500) 165 | return 166 | } 167 | respErr = ipt.Insert("filter", vars["chain"], position, rulespecs...) 168 | } else { 169 | respErr = ipt.Append("filter", vars["chain"], rulespecs...) 170 | } 171 | if respErr != nil { 172 | w.WriteHeader(http.StatusBadRequest) 173 | fmt.Fprintln(w, respErr) 174 | } 175 | } 176 | 177 | // DELETE /rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 178 | func delRules(w http.ResponseWriter, r *http.Request) { 179 | if *htpasswdfile != "" { 180 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 181 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 182 | usercheck := authenticator.CheckAuth(r) 183 | if usercheck == "" { 184 | w.WriteHeader(http.StatusUnauthorized) 185 | return 186 | } 187 | } 188 | rulespecs := ruleGenerate(r) 189 | ipt, err := iptables.New() 190 | if err != nil { 191 | http.Error(w, err.Error(), 500) 192 | return 193 | } 194 | if ipt.HasWait { 195 | rulespecs = append(rulespecs, "--wait") 196 | } 197 | vars := mux.Vars(r) 198 | respErr = ipt.Delete("filter", vars["chain"], rulespecs...) 199 | if respErr != nil { 200 | w.WriteHeader(http.StatusBadRequest) 201 | fmt.Fprintln(w, respErr) 202 | } 203 | } 204 | 205 | // GET /rules/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00 206 | func checkRules(w http.ResponseWriter, r *http.Request) { 207 | if *htpasswdfile != "" { 208 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 209 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 210 | usercheck := authenticator.CheckAuth(r) 211 | if usercheck == "" { 212 | w.WriteHeader(http.StatusUnauthorized) 213 | return 214 | } 215 | } 216 | rulespecs := ruleGenerate(r) 217 | ipt, err := iptables.New() 218 | if err != nil { 219 | http.Error(w, err.Error(), 500) 220 | return 221 | } 222 | if ipt.HasWait { 223 | rulespecs = append(rulespecs, "--wait") 224 | } 225 | if r.URL.Query().Get("position") != "" { 226 | posRules, err := checkPosRules(r) 227 | if err != nil { 228 | http.Error(w, err.Error(), 500) 229 | return 230 | } 231 | switch { 232 | case len(posRules) == 0: 233 | w.WriteHeader(http.StatusNotFound) 234 | return 235 | case len(posRules) != 1: 236 | w.WriteHeader(http.StatusConflict) 237 | return 238 | case posRules[0] == r.URL.Query().Get("position"): 239 | return 240 | default: 241 | w.WriteHeader(http.StatusNotFound) 242 | return 243 | } 244 | } else { 245 | vars := mux.Vars(r) 246 | respStr, respErr := ipt.Exists("filter", vars["chain"], rulespecs...) 247 | if respErr != nil { 248 | w.WriteHeader(http.StatusBadRequest) 249 | fmt.Fprintln(w, respErr) 250 | return 251 | } 252 | if !respStr { 253 | w.WriteHeader(http.StatusNotFound) 254 | return 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /raw.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func rawGenerate(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | var specEnd []string 18 | 19 | if r.URL.Query().Get("sports") != "" { 20 | specEnd = append(specEnd, "-m", "multiport", "--sports", r.URL.Query().Get("sports")) 21 | } 22 | if r.URL.Query().Get("dports") != "" { 23 | specEnd = append(specEnd, "-m", "multiport", "--dports", r.URL.Query().Get("dports")) 24 | } 25 | if r.URL.Query().Get("notrack") != "" { 26 | specEnd = append(specEnd, "--notrack") 27 | } 28 | if (r.URL.Query().Get("tcpflag1") != "") && (r.URL.Query().Get("tcpflag2") != "") && (vars["proto"] == tcpStr) { 29 | tcpflag := []string{"--tcp-flags", r.URL.Query().Get("tcpflag1"), r.URL.Query().Get("tcpflag2")} 30 | specEnd = append(specEnd, tcpflag...) 31 | } 32 | if r.URL.Query().Get("tcpmss") != "" { 33 | specEnd = append(specEnd, "-m", "tcpmss", "--mss", r.URL.Query().Get("tcpmss")) 34 | } 35 | if vars["iface_in"] != "*" { 36 | specEnd = append(specEnd, "-i", vars["iface_in"]) 37 | } 38 | if vars["iface_out"] != "*" { 39 | specEnd = append(specEnd, "-o", vars["iface_out"]) 40 | } 41 | srcRange := strings.Contains(vars["source"], "-") 42 | dstRange := strings.Contains(vars["destination"], "-") 43 | ruleSpecs := []string{"-p", vars["proto"]} 44 | if srcRange { 45 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_32", "", -1)) 46 | } else { 47 | ruleSpecs = append(ruleSpecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 48 | } 49 | if dstRange { 50 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_32", "", -1)) 51 | } else { 52 | ruleSpecs = append(ruleSpecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 53 | } 54 | ruleSpecs = append(ruleSpecs, "-j", vars["action"]) 55 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 56 | ruleSpecs = append(ruleSpecs, "--log-prefix", r.URL.Query().Get("log-prefix")) 57 | } 58 | ruleSpecs = append(ruleSpecs, specEnd...) 59 | return ruleSpecs 60 | } 61 | 62 | func checkPosRaw(r *http.Request) ([]string, error) { 63 | vars := mux.Vars(r) 64 | var linenumber []string 65 | 66 | line := []string{vars["action"], vars["proto"], "--"} 67 | line = append(line, vars["iface_in"], vars["iface_out"]) 68 | 69 | srcRange := strings.Contains(vars["source"], "-") 70 | if srcRange { 71 | line = append(line, "0.0.0.0/0") 72 | } else { 73 | source32 := strings.Contains(vars["source"], "_32") 74 | if source32 { 75 | line = append(line, strings.Replace(vars["source"], "_32", "", -1)) 76 | } else { 77 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 78 | } 79 | } 80 | 81 | dstRange := strings.Contains(vars["destination"], "-") 82 | if dstRange { 83 | line = append(line, "0.0.0.0/0") 84 | } else { 85 | destination32 := strings.Contains(vars["destination"], "_32") 86 | if destination32 { 87 | line = append(line, strings.Replace(vars["destination"], "_32", "", -1)) 88 | } else { 89 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 90 | } 91 | } 92 | if srcRange { 93 | line = append(line, "source", "IP", "range", strings.Replace(vars["source"], "_32", "", -1)) 94 | } 95 | if dstRange { 96 | line = append(line, "destination", "IP", "range", strings.Replace(vars["destination"], "_32", "", -1)) 97 | } 98 | if r.URL.Query().Get("sports") != "" { 99 | line = append(line, "multiport", "sports", r.URL.Query().Get("sports")) 100 | } 101 | if r.URL.Query().Get("dports") != "" { 102 | line = append(line, "multiport", "dports", r.URL.Query().Get("dports")) 103 | } 104 | if (r.URL.Query().Get("tcpflag1") != "") && (r.URL.Query().Get("tcpflag2") != "") && (vars["proto"] == tcpStr) { 105 | line = append(line, tcpStr) 106 | flags := "" 107 | if r.URL.Query().Get("tcpflag1") == SYNStr { 108 | flags = "flags:0x02/" 109 | } 110 | if (r.URL.Query().Get("tcpflag1") == defaultFlagsMask) || (r.URL.Query().Get("tcpflag1") == defaultFlagsMask2) { 111 | flags = "flags:0x17/" 112 | } 113 | if r.URL.Query().Get("tcpflag2") == SYNStr { 114 | flags = strings.Join([]string{flags, "0x02"}, "") 115 | } 116 | line = append(line, flags) 117 | } 118 | if r.URL.Query().Get("tcpmss") != "" { 119 | line = append(line, "tcpmss", "match", r.URL.Query().Get("tcpmss")) 120 | } 121 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 122 | line = append(line, "LOG", "flags", "0", "level", "4", "prefix", strings.Join([]string{"\"", r.URL.Query().Get("log-prefix"), "\""}, "")) 123 | } 124 | ipt, err := iptables.New() 125 | if err != nil { 126 | return nil, err 127 | } 128 | args := []string{"-t", "raw", "-vnL", vars["chain"], "--line-numbers"} 129 | if ipt.HasWait { 130 | args = append(args, "--wait") 131 | } 132 | raws, err := ipt.ExecuteList(args) 133 | if err != nil { 134 | return nil, err 135 | } 136 | for i := 0; i < len(raws); i++ { 137 | rawsSlice := strings.Fields(raws[i]) 138 | rawsSliceNoVerb := rawsSlice[3:] 139 | if reflect.DeepEqual(line, rawsSliceNoVerb) { 140 | linenumber = append(linenumber, rawsSlice[0]) 141 | } 142 | } 143 | return linenumber, nil 144 | } 145 | 146 | // PUT /raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 147 | func addRaw(w http.ResponseWriter, r *http.Request) { 148 | if *htpasswdfile != "" { 149 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 150 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 151 | usercheck := authenticator.CheckAuth(r) 152 | if usercheck == "" { 153 | w.WriteHeader(http.StatusUnauthorized) 154 | return 155 | } 156 | } 157 | vars := mux.Vars(r) 158 | ipt, err := iptables.New() 159 | if err != nil { 160 | http.Error(w, err.Error(), 500) 161 | return 162 | } 163 | rulespecs := rawGenerate(r) 164 | if ipt.HasWait { 165 | rulespecs = append(rulespecs, "--wait") 166 | } 167 | if r.URL.Query().Get("position") != "" { 168 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 169 | if err != nil { 170 | http.Error(w, err.Error(), 500) 171 | return 172 | } 173 | respErr = ipt.Insert("raw", vars["chain"], position, rulespecs...) 174 | } else { 175 | respErr = ipt.Append("raw", vars["chain"], rulespecs...) 176 | } 177 | if respErr != nil { 178 | w.WriteHeader(http.StatusBadRequest) 179 | fmt.Fprintln(w, respErr) 180 | } 181 | } 182 | 183 | // DELTE /raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 184 | func delRaw(w http.ResponseWriter, r *http.Request) { 185 | if *htpasswdfile != "" { 186 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 187 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 188 | usercheck := authenticator.CheckAuth(r) 189 | if usercheck == "" { 190 | w.WriteHeader(http.StatusUnauthorized) 191 | return 192 | } 193 | } 194 | vars := mux.Vars(r) 195 | ipt, err := iptables.New() 196 | if err != nil { 197 | http.Error(w, err.Error(), 500) 198 | return 199 | } 200 | rulespecs := rawGenerate(r) 201 | if ipt.HasWait { 202 | rulespecs = append(rulespecs, "--wait") 203 | } 204 | respErr = ipt.Delete("raw", vars["chain"], rulespecs...) 205 | if respErr != nil { 206 | w.WriteHeader(http.StatusBadRequest) 207 | fmt.Fprintln(w, respErr) 208 | } 209 | } 210 | 211 | // GET /raw/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 212 | func checkRaw(w http.ResponseWriter, r *http.Request) { 213 | if *htpasswdfile != "" { 214 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 215 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 216 | usercheck := authenticator.CheckAuth(r) 217 | if usercheck == "" { 218 | w.WriteHeader(http.StatusUnauthorized) 219 | return 220 | } 221 | } 222 | ipt, err := iptables.New() 223 | if err != nil { 224 | http.Error(w, err.Error(), 500) 225 | return 226 | } 227 | rulespecs := rawGenerate(r) 228 | if ipt.HasWait { 229 | rulespecs = append(rulespecs, "--wait") 230 | } 231 | if r.URL.Query().Get("position") != "" { 232 | if r.URL.Query().Get("tcpflag1") != "" { 233 | if (r.URL.Query().Get("tcpflag1") != defaultFlagsMask) && (r.URL.Query().Get("tcpflag1") != SYNStr) && (r.URL.Query().Get("tcpflag1") != defaultFlagsMask2) { 234 | w.WriteHeader(http.StatusBadRequest) 235 | fmt.Fprintln(w, "tcpflag", r.URL.Query().Get("tcpflag1"), "and position not compatible") 236 | return 237 | } 238 | } 239 | if r.URL.Query().Get("tcpflag2") != "" { 240 | if r.URL.Query().Get("tcpflag2") != SYNStr { 241 | w.WriteHeader(http.StatusBadRequest) 242 | fmt.Fprintln(w, "tcpflag", r.URL.Query().Get("tcpflag2"), "and position not compatible") 243 | return 244 | } 245 | } 246 | posRaw, err := checkPosRaw(r) 247 | if err != nil { 248 | http.Error(w, err.Error(), 500) 249 | return 250 | } 251 | switch { 252 | case len(posRaw) == 0: 253 | w.WriteHeader(http.StatusNotFound) 254 | return 255 | case len(posRaw) != 1: 256 | w.WriteHeader(http.StatusConflict) 257 | return 258 | case posRaw[0] == r.URL.Query().Get("position"): 259 | return 260 | default: 261 | w.WriteHeader(http.StatusNotFound) 262 | return 263 | } 264 | } else { 265 | vars := mux.Vars(r) 266 | respStr, respErr := ipt.Exists("raw", vars["chain"], rulespecs...) 267 | if respErr != nil { 268 | w.WriteHeader(http.StatusBadRequest) 269 | fmt.Fprintln(w, respErr) 270 | return 271 | } 272 | if !respStr { 273 | w.WriteHeader(http.StatusNotFound) 274 | return 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /raw_v6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func rawGenerateV6(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | var specEnd []string 18 | 19 | if r.URL.Query().Get("sports") != "" { 20 | specEnd = append(specEnd, "-m", "multiport", "--sports", r.URL.Query().Get("sports")) 21 | } 22 | if r.URL.Query().Get("dports") != "" { 23 | specEnd = append(specEnd, "-m", "multiport", "--dports", r.URL.Query().Get("dports")) 24 | } 25 | if r.URL.Query().Get("notrack") != "" { 26 | specEnd = append(specEnd, "--notrack") 27 | } 28 | if (r.URL.Query().Get("tcpflag1") != "") && (r.URL.Query().Get("tcpflag2") != "") && (vars["proto"] == tcpStr) { 29 | tcpflag := []string{"--tcp-flags", r.URL.Query().Get("tcpflag1"), r.URL.Query().Get("tcpflag2")} 30 | specEnd = append(specEnd, tcpflag...) 31 | } 32 | if r.URL.Query().Get("tcpmss") != "" { 33 | specEnd = append(specEnd, "-m", "tcpmss", "--mss", r.URL.Query().Get("tcpmss")) 34 | } 35 | if vars["iface_in"] != "*" { 36 | specEnd = append(specEnd, "-i", vars["iface_in"]) 37 | } 38 | if vars["iface_out"] != "*" { 39 | specEnd = append(specEnd, "-o", vars["iface_out"]) 40 | } 41 | srcRange := strings.Contains(vars["source"], "-") 42 | dstRange := strings.Contains(vars["destination"], "-") 43 | ruleSpecs := []string{"-p", vars["proto"]} 44 | if srcRange { 45 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_128", "", -1)) 46 | } else { 47 | ruleSpecs = append(ruleSpecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 48 | } 49 | if dstRange { 50 | ruleSpecs = append(ruleSpecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_128", "", -1)) 51 | } else { 52 | ruleSpecs = append(ruleSpecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 53 | } 54 | ruleSpecs = append(ruleSpecs, "-j", vars["action"]) 55 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 56 | ruleSpecs = append(ruleSpecs, "--log-prefix", r.URL.Query().Get("log-prefix")) 57 | } 58 | ruleSpecs = append(ruleSpecs, specEnd...) 59 | return ruleSpecs 60 | } 61 | 62 | func checkPosRawV6(r *http.Request) ([]string, error) { 63 | vars := mux.Vars(r) 64 | var linenumber []string 65 | 66 | line := []string{vars["action"], vars["proto"]} 67 | line = append(line, vars["iface_in"], vars["iface_out"]) 68 | 69 | srcRange := strings.Contains(vars["source"], "-") 70 | if srcRange { 71 | line = append(line, "::/0") 72 | } else { 73 | source128 := strings.Contains(vars["source"], "_128") 74 | if source128 { 75 | line = append(line, strings.Replace(vars["source"], "_128", "", -1)) 76 | } else { 77 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 78 | } 79 | } 80 | 81 | dstRange := strings.Contains(vars["destination"], "-") 82 | if dstRange { 83 | line = append(line, "::/0") 84 | } else { 85 | destination128 := strings.Contains(vars["destination"], "_128") 86 | if destination128 { 87 | line = append(line, strings.Replace(vars["destination"], "_128", "", -1)) 88 | } else { 89 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 90 | } 91 | } 92 | if srcRange { 93 | line = append(line, "source", "IP", "range", strings.Replace(vars["source"], "_128", "", -1)) 94 | } 95 | if dstRange { 96 | line = append(line, "destination", "IP", "range", strings.Replace(vars["destination"], "_128", "", -1)) 97 | } 98 | if r.URL.Query().Get("sports") != "" { 99 | line = append(line, "multiport", "sports", r.URL.Query().Get("sports")) 100 | } 101 | if r.URL.Query().Get("dports") != "" { 102 | line = append(line, "multiport", "dports", r.URL.Query().Get("dports")) 103 | } 104 | if (r.URL.Query().Get("tcpflag1") != "") && (r.URL.Query().Get("tcpflag2") != "") && (vars["proto"] == tcpStr) { 105 | line = append(line, tcpStr) 106 | flags := "" 107 | if r.URL.Query().Get("tcpflag1") == SYNStr { 108 | flags = "flags:0x02/" 109 | } 110 | if (r.URL.Query().Get("tcpflag1") == defaultFlagsMask) || (r.URL.Query().Get("tcpflag1") == defaultFlagsMask2) { 111 | flags = "flags:0x17/" 112 | } 113 | if r.URL.Query().Get("tcpflag2") == SYNStr { 114 | flags = strings.Join([]string{flags, "0x02"}, "") 115 | } 116 | line = append(line, flags) 117 | } 118 | if r.URL.Query().Get("tcpmss") != "" { 119 | line = append(line, "tcpmss", "match", r.URL.Query().Get("tcpmss")) 120 | } 121 | if (r.URL.Query().Get("log-prefix") != "") && vars["action"] == logAct { 122 | line = append(line, "LOG", "flags", "0", "level", "4", "prefix", strings.Join([]string{"\"", r.URL.Query().Get("log-prefix"), "\""}, "")) 123 | } 124 | ipt, err := iptables.NewWithProtocol(v6) 125 | if err != nil { 126 | return nil, err 127 | } 128 | args := []string{"-t", "raw", "-vnL", vars["chain"], "--line-numbers"} 129 | if ipt.HasWait { 130 | args = append(args, "--wait") 131 | } 132 | raws, err := ipt.ExecuteList(args) 133 | if err != nil { 134 | return nil, err 135 | } 136 | for i := 0; i < len(raws); i++ { 137 | rawsSlice := strings.Fields(raws[i]) 138 | rawsSliceNoVerb := rawsSlice[3:] 139 | if reflect.DeepEqual(line, rawsSliceNoVerb) { 140 | linenumber = append(linenumber, rawsSlice[0]) 141 | } 142 | } 143 | return linenumber, nil 144 | } 145 | 146 | // PUT /raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 147 | func addRawV6(w http.ResponseWriter, r *http.Request) { 148 | if *htpasswdfile != "" { 149 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 150 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 151 | usercheck := authenticator.CheckAuth(r) 152 | if usercheck == "" { 153 | w.WriteHeader(http.StatusUnauthorized) 154 | return 155 | } 156 | } 157 | vars := mux.Vars(r) 158 | ipt, err := iptables.NewWithProtocol(v6) 159 | if err != nil { 160 | http.Error(w, err.Error(), 500) 161 | return 162 | } 163 | rulespecs := rawGenerateV6(r) 164 | if ipt.HasWait { 165 | rulespecs = append(rulespecs, "--wait") 166 | } 167 | if r.URL.Query().Get("position") != "" { 168 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 169 | if err != nil { 170 | http.Error(w, err.Error(), 500) 171 | return 172 | } 173 | respErr = ipt.Insert("raw", vars["chain"], position, rulespecs...) 174 | } else { 175 | respErr = ipt.Append("raw", vars["chain"], rulespecs...) 176 | } 177 | if respErr != nil { 178 | w.WriteHeader(http.StatusBadRequest) 179 | fmt.Fprintln(w, respErr) 180 | } 181 | } 182 | 183 | // DELTE /raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 184 | func delRawV6(w http.ResponseWriter, r *http.Request) { 185 | if *htpasswdfile != "" { 186 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 187 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 188 | usercheck := authenticator.CheckAuth(r) 189 | if usercheck == "" { 190 | w.WriteHeader(http.StatusUnauthorized) 191 | return 192 | } 193 | } 194 | vars := mux.Vars(r) 195 | ipt, err := iptables.NewWithProtocol(v6) 196 | if err != nil { 197 | http.Error(w, err.Error(), 500) 198 | return 199 | } 200 | rulespecs := rawGenerateV6(r) 201 | if ipt.HasWait { 202 | rulespecs = append(rulespecs, "--wait") 203 | } 204 | respErr = ipt.Delete("raw", vars["chain"], rulespecs...) 205 | if respErr != nil { 206 | w.WriteHeader(http.StatusBadRequest) 207 | fmt.Fprintln(w, respErr) 208 | } 209 | } 210 | 211 | // GET /raw_v6/{action}/{chain}/{proto}/{iface_in}/{iface_out}/{source}/{destination}/?sports=00&dports=00&tcpflag1=XYZ&tcpflag2=Y¬rack=true 212 | func checkRawV6(w http.ResponseWriter, r *http.Request) { 213 | if *htpasswdfile != "" { 214 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 215 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 216 | usercheck := authenticator.CheckAuth(r) 217 | if usercheck == "" { 218 | w.WriteHeader(http.StatusUnauthorized) 219 | return 220 | } 221 | } 222 | ipt, err := iptables.NewWithProtocol(v6) 223 | if err != nil { 224 | http.Error(w, err.Error(), 500) 225 | return 226 | } 227 | rulespecs := rawGenerateV6(r) 228 | if ipt.HasWait { 229 | rulespecs = append(rulespecs, "--wait") 230 | } 231 | if r.URL.Query().Get("position") != "" { 232 | if r.URL.Query().Get("tcpflag1") != "" { 233 | if (r.URL.Query().Get("tcpflag1") != defaultFlagsMask) && (r.URL.Query().Get("tcpflag1") != SYNStr) && (r.URL.Query().Get("tcpflag1") != defaultFlagsMask2) { 234 | w.WriteHeader(http.StatusBadRequest) 235 | fmt.Fprintln(w, "tcpflag", r.URL.Query().Get("tcpflag1"), "and position not compatible") 236 | return 237 | } 238 | } 239 | if r.URL.Query().Get("tcpflag2") != "" { 240 | if r.URL.Query().Get("tcpflag2") != SYNStr { 241 | w.WriteHeader(http.StatusBadRequest) 242 | fmt.Fprintln(w, "tcpflag", r.URL.Query().Get("tcpflag2"), "and position not compatible") 243 | return 244 | } 245 | } 246 | posRaw, err := checkPosRawV6(r) 247 | if err != nil { 248 | http.Error(w, err.Error(), 500) 249 | return 250 | } 251 | switch { 252 | case len(posRaw) == 0: 253 | w.WriteHeader(http.StatusNotFound) 254 | return 255 | case len(posRaw) != 1: 256 | w.WriteHeader(http.StatusConflict) 257 | return 258 | case posRaw[0] == r.URL.Query().Get("position"): 259 | return 260 | default: 261 | w.WriteHeader(http.StatusNotFound) 262 | return 263 | } 264 | } else { 265 | vars := mux.Vars(r) 266 | respStr, respErr := ipt.Exists("raw", vars["chain"], rulespecs...) 267 | if respErr != nil { 268 | w.WriteHeader(http.StatusBadRequest) 269 | fmt.Fprintln(w, respErr) 270 | return 271 | } 272 | if !respStr { 273 | w.WriteHeader(http.StatusNotFound) 274 | return 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /nat.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func dnatGenerate(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | rulespecs := append([]string{"-p", vars["proto"], "-i", vars["iface"]}) 18 | if r.URL.Query().Get("except") == trueStr { 19 | rulespecs = append(rulespecs, "!") 20 | } 21 | srcRange := strings.Contains(vars["source"], "-") 22 | dstRange := strings.Contains(vars["destination"], "-") 23 | if srcRange { 24 | rulespecs = append(rulespecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_32", "", -1)) 25 | } else { 26 | rulespecs = append(rulespecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 27 | } 28 | if dstRange { 29 | rulespecs = append(rulespecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_32", "", -1)) 30 | } else { 31 | rulespecs = append(rulespecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 32 | } 33 | rulespecs = append(rulespecs, "-j", "DNAT", "--to-destination", vars["nat_final"]) 34 | if r.URL.Query().Get("dport") != "" { 35 | rulespecs = append(rulespecs, "--dport", r.URL.Query().Get("dport")) 36 | } 37 | if r.URL.Query().Get("nth_every") != "" { 38 | rulespecs = append(rulespecs, "-m", "statistic", "--mode", "nth", "--every", r.URL.Query().Get("nth_every"), "--packet", r.URL.Query().Get("nth_packet")) 39 | } 40 | return rulespecs 41 | } 42 | 43 | func snatGenerate(r *http.Request) []string { 44 | vars := mux.Vars(r) 45 | rulespecs := append([]string{"-p", vars["proto"], "-o", vars["iface"]}) 46 | srcRange := strings.Contains(vars["source"], "-") 47 | dstRange := strings.Contains(vars["destination"], "-") 48 | if srcRange { 49 | rulespecs = append(rulespecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_32", "", -1)) 50 | } else { 51 | rulespecs = append(rulespecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 52 | } 53 | if r.URL.Query().Get("except") == trueStr { 54 | rulespecs = append(rulespecs, "!") 55 | } 56 | if dstRange { 57 | rulespecs = append(rulespecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_32", "", -1)) 58 | } else { 59 | rulespecs = append(rulespecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 60 | } 61 | rulespecs = append(rulespecs, "-j", "SNAT", "--to-source", vars["nat_final"]) 62 | if r.URL.Query().Get("dport") != "" { 63 | rulespecs = append(rulespecs, "--dport", r.URL.Query().Get("dport")) 64 | } 65 | if r.URL.Query().Get("nth_every") != "" { 66 | rulespecs = append(rulespecs, "-m", "statistic", "--mode", "nth", "--every", r.URL.Query().Get("nth_every"), "--packet", r.URL.Query().Get("nth_packet")) 67 | } 68 | return rulespecs 69 | } 70 | 71 | func CheckPosNat(r *http.Request) ([]string, error) { 72 | vars := mux.Vars(r) 73 | var linenumber []string 74 | var line []string 75 | 76 | if vars["action"] == dnatAct { 77 | line = append(line, "DNAT", vars["proto"], "--", vars["iface"], "*") 78 | } 79 | if vars["action"] == snatAct { 80 | line = append(line, "SNAT", vars["proto"], "--", "*", vars["iface"]) 81 | } 82 | source32 := strings.Contains(vars["source"], "_32") 83 | destination32 := strings.Contains(vars["destination"], "_32") 84 | 85 | if source32 { 86 | if (vars["action"] == dnatAct) && (r.URL.Query().Get("except") == trueStr) { 87 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["source"], "_32", "", -1)}, "")) 88 | } else { 89 | line = append(line, strings.Replace(vars["source"], "_32", "", -1)) 90 | } 91 | } else { 92 | if (vars["action"] == dnatAct) && (r.URL.Query().Get("except") == trueStr) { 93 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["source"], "_", "/", -1)}, "")) 94 | } else { 95 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 96 | } 97 | } 98 | if destination32 { 99 | if (vars["action"] == snatAct) && (r.URL.Query().Get("except") == trueStr) { 100 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["destination"], "_32", "", -1)}, "")) 101 | } else { 102 | line = append(line, strings.Replace(vars["destination"], "_32", "", -1)) 103 | } 104 | } else { 105 | if (vars["action"] == snatAct) && (r.URL.Query().Get("except") == trueStr) { 106 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["destination"], "_", "/", -1)}, "")) 107 | } else { 108 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 109 | } 110 | } 111 | if r.URL.Query().Get("dport") != "" { 112 | line = append(line, "tcp", strings.Join([]string{"dpt:", r.URL.Query().Get("dport")}, "")) 113 | } 114 | if r.URL.Query().Get("nth_every") != "" { 115 | if r.URL.Query().Get("nth_packet") == "0" { 116 | line = append(line, "statistic", "mode", "nth", "every", r.URL.Query().Get("nth_every")) 117 | } else { 118 | line = append(line, "statistic", "mode", "nth", "every", r.URL.Query().Get("nth_every"), "packet", r.URL.Query().Get("nth_packet")) 119 | } 120 | } 121 | line = append(line, strings.Join([]string{"to:", vars["nat_final"]}, "")) 122 | 123 | ipt, err := iptables.New() 124 | if err != nil { 125 | return nil, err 126 | } 127 | args := []string{"-t", "nat", "-vnL", vars["chain"], "--line-numbers"} 128 | if ipt.HasWait { 129 | args = append(args, "--wait") 130 | } 131 | nats, err := ipt.ExecuteList(args) 132 | if err != nil { 133 | return nil, err 134 | } 135 | for i := 0; i < len(nats); i++ { 136 | natsSlice := strings.Fields(nats[i]) 137 | natsSliceNoVerb := natsSlice[3:] 138 | if reflect.DeepEqual(line, natsSliceNoVerb) { 139 | linenumber = append(linenumber, natsSlice[0]) 140 | } 141 | } 142 | return linenumber, nil 143 | } 144 | 145 | // PUT /nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 146 | func addNat(w http.ResponseWriter, r *http.Request) { 147 | if *htpasswdfile != "" { 148 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 149 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 150 | usercheck := authenticator.CheckAuth(r) 151 | if usercheck == "" { 152 | w.WriteHeader(http.StatusUnauthorized) 153 | return 154 | } 155 | } 156 | vars := mux.Vars(r) 157 | ipt, err := iptables.New() 158 | if err != nil { 159 | http.Error(w, err.Error(), 500) 160 | return 161 | } 162 | var rulespecs []string 163 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 164 | if r.URL.Query().Get("nth_every") == "" { 165 | w.WriteHeader(http.StatusBadRequest) 166 | fmt.Fprintln(w, "Missing nth every") 167 | return 168 | } 169 | if r.URL.Query().Get("nth_packet") == "" { 170 | w.WriteHeader(http.StatusBadRequest) 171 | fmt.Fprintln(w, "Missing nth packet") 172 | return 173 | } 174 | } 175 | switch vars["action"] { 176 | case dnatAct: 177 | rulespecs = dnatGenerate(r) 178 | case snatAct: 179 | rulespecs = snatGenerate(r) 180 | default: 181 | w.WriteHeader(http.StatusNotFound) 182 | return 183 | } 184 | if ipt.HasWait { 185 | rulespecs = append(rulespecs, "--wait") 186 | } 187 | if r.URL.Query().Get("position") != "" { 188 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 189 | if err != nil { 190 | http.Error(w, err.Error(), 500) 191 | return 192 | } 193 | respErr = ipt.Insert("nat", vars["chain"], position, rulespecs...) 194 | } else { 195 | respErr = ipt.Append("nat", vars["chain"], rulespecs...) 196 | } 197 | if respErr != nil { 198 | w.WriteHeader(http.StatusBadRequest) 199 | fmt.Fprintln(w, respErr) 200 | } 201 | } 202 | 203 | // DELETE /nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 204 | func delNat(w http.ResponseWriter, r *http.Request) { 205 | if *htpasswdfile != "" { 206 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 207 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 208 | usercheck := authenticator.CheckAuth(r) 209 | if usercheck == "" { 210 | w.WriteHeader(http.StatusUnauthorized) 211 | return 212 | } 213 | } 214 | vars := mux.Vars(r) 215 | 216 | ipt, err := iptables.New() 217 | if err != nil { 218 | http.Error(w, err.Error(), 500) 219 | return 220 | } 221 | var rulespecs []string 222 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 223 | if r.URL.Query().Get("nth_every") == "" { 224 | w.WriteHeader(http.StatusBadRequest) 225 | fmt.Fprintln(w, "Missing nth every") 226 | return 227 | } 228 | if r.URL.Query().Get("nth_packet") == "" { 229 | w.WriteHeader(http.StatusBadRequest) 230 | fmt.Fprintln(w, "Missing nth packet") 231 | return 232 | } 233 | } 234 | switch vars["action"] { 235 | case dnatAct: 236 | rulespecs = dnatGenerate(r) 237 | case snatAct: 238 | rulespecs = snatGenerate(r) 239 | default: 240 | w.WriteHeader(http.StatusNotFound) 241 | return 242 | } 243 | if ipt.HasWait { 244 | rulespecs = append(rulespecs, "--wait") 245 | } 246 | respErr = ipt.Delete("nat", vars["chain"], rulespecs...) 247 | if respErr != nil { 248 | w.WriteHeader(http.StatusBadRequest) 249 | fmt.Fprintln(w, respErr) 250 | } 251 | } 252 | 253 | // GET /nat/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 254 | func checkNat(w http.ResponseWriter, r *http.Request) { 255 | if *htpasswdfile != "" { 256 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 257 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 258 | usercheck := authenticator.CheckAuth(r) 259 | if usercheck == "" { 260 | w.WriteHeader(http.StatusUnauthorized) 261 | return 262 | } 263 | } 264 | vars := mux.Vars(r) 265 | 266 | ipt, err := iptables.New() 267 | if err != nil { 268 | http.Error(w, err.Error(), 500) 269 | return 270 | } 271 | if r.URL.Query().Get("position") != "" { 272 | posNat, err := CheckPosNat(r) 273 | 274 | if err != nil { 275 | http.Error(w, err.Error(), 500) 276 | return 277 | } 278 | switch { 279 | case len(posNat) == 0: 280 | w.WriteHeader(http.StatusNotFound) 281 | return 282 | case len(posNat) != 1: 283 | w.WriteHeader(http.StatusConflict) 284 | return 285 | case posNat[0] == r.URL.Query().Get("position"): 286 | return 287 | default: 288 | w.WriteHeader(http.StatusNotFound) 289 | return 290 | } 291 | } 292 | var rulespecs []string 293 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 294 | if r.URL.Query().Get("nth_every") == "" { 295 | w.WriteHeader(http.StatusBadRequest) 296 | fmt.Fprintln(w, "Missing nth every") 297 | return 298 | } 299 | if r.URL.Query().Get("nth_packet") == "" { 300 | w.WriteHeader(http.StatusBadRequest) 301 | fmt.Fprintln(w, "Missing nth packet") 302 | return 303 | } 304 | } 305 | switch vars["action"] { 306 | case dnatAct: 307 | rulespecs = dnatGenerate(r) 308 | case snatAct: 309 | rulespecs = snatGenerate(r) 310 | default: 311 | w.WriteHeader(http.StatusNotFound) 312 | return 313 | } 314 | if ipt.HasWait { 315 | rulespecs = append(rulespecs, "--wait") 316 | } 317 | respStr, respErr := ipt.Exists("nat", vars["chain"], rulespecs...) 318 | if respErr != nil { 319 | w.WriteHeader(http.StatusBadRequest) 320 | fmt.Fprintln(w, respErr) 321 | } 322 | if !respStr { 323 | w.WriteHeader(http.StatusNotFound) 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /nat_v6.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | 10 | auth "github.com/abbot/go-http-auth" 11 | "github.com/gorilla/mux" 12 | "github.com/jeremmfr/go-iptables/iptables" 13 | ) 14 | 15 | func dnatGenerateV6(r *http.Request) []string { 16 | vars := mux.Vars(r) 17 | rulespecs := append([]string{"-p", vars["proto"], "-i", vars["iface"]}) 18 | if r.URL.Query().Get("except") == trueStr { 19 | rulespecs = append(rulespecs, "!") 20 | } 21 | srcRange := strings.Contains(vars["source"], "-") 22 | dstRange := strings.Contains(vars["destination"], "-") 23 | if srcRange { 24 | rulespecs = append(rulespecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_128", "", -1)) 25 | } else { 26 | rulespecs = append(rulespecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 27 | } 28 | if dstRange { 29 | rulespecs = append(rulespecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_128", "", -1)) 30 | } else { 31 | rulespecs = append(rulespecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 32 | } 33 | rulespecs = append(rulespecs, "-j", "DNAT", "--to-destination", vars["nat_final"]) 34 | if r.URL.Query().Get("dport") != "" { 35 | rulespecs = append(rulespecs, "--dport", r.URL.Query().Get("dport")) 36 | } 37 | if r.URL.Query().Get("nth_every") != "" { 38 | rulespecs = append(rulespecs, "-m", "statistic", "--mode", "nth", "--every", r.URL.Query().Get("nth_every"), "--packet", r.URL.Query().Get("nth_packet")) 39 | } 40 | return rulespecs 41 | } 42 | 43 | func snatGenerateV6(r *http.Request) []string { 44 | vars := mux.Vars(r) 45 | rulespecs := append([]string{"-p", vars["proto"], "-o", vars["iface"]}) 46 | srcRange := strings.Contains(vars["source"], "-") 47 | dstRange := strings.Contains(vars["destination"], "-") 48 | if srcRange { 49 | rulespecs = append(rulespecs, "-m", "iprange", "--src-range", strings.Replace(vars["source"], "_128", "", -1)) 50 | } else { 51 | rulespecs = append(rulespecs, "-s", strings.Replace(vars["source"], "_", "/", -1)) 52 | } 53 | if r.URL.Query().Get("except") == trueStr { 54 | rulespecs = append(rulespecs, "!") 55 | } 56 | if dstRange { 57 | rulespecs = append(rulespecs, "-m", "iprange", "--dst-range", strings.Replace(vars["destination"], "_128", "", -1)) 58 | } else { 59 | rulespecs = append(rulespecs, "-d", strings.Replace(vars["destination"], "_", "/", -1)) 60 | } 61 | rulespecs = append(rulespecs, "-j", "SNAT", "--to-source", vars["nat_final"]) 62 | if r.URL.Query().Get("dport") != "" { 63 | rulespecs = append(rulespecs, "--dport", r.URL.Query().Get("dport")) 64 | } 65 | if r.URL.Query().Get("nth_every") != "" { 66 | rulespecs = append(rulespecs, "-m", "statistic", "--mode", "nth", "--every", r.URL.Query().Get("nth_every"), "--packet", r.URL.Query().Get("nth_packet")) 67 | } 68 | return rulespecs 69 | } 70 | 71 | func checkPosNatV6(r *http.Request) ([]string, error) { 72 | vars := mux.Vars(r) 73 | var linenumber []string 74 | var line []string 75 | 76 | if vars["action"] == dnatAct { 77 | line = append(line, "DNAT", vars["proto"], vars["iface"], "*") 78 | } 79 | if vars["action"] == snatAct { 80 | line = append(line, "SNAT", vars["proto"], "*", vars["iface"]) 81 | } 82 | source128 := strings.Contains(vars["source"], "_128") 83 | destination128 := strings.Contains(vars["destination"], "_128") 84 | 85 | if source128 { 86 | if (vars["action"] == dnatAct) && (r.URL.Query().Get("except") == trueStr) { 87 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["source"], "_128", "", -1)}, "")) 88 | } else { 89 | line = append(line, strings.Replace(vars["source"], "_128", "", -1)) 90 | } 91 | } else { 92 | if (vars["action"] == dnatAct) && (r.URL.Query().Get("except") == trueStr) { 93 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["source"], "_", "/", -1)}, "")) 94 | } else { 95 | line = append(line, strings.Replace(vars["source"], "_", "/", -1)) 96 | } 97 | } 98 | if destination128 { 99 | if (vars["action"] == snatAct) && (r.URL.Query().Get("except") == trueStr) { 100 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["destination"], "_128", "", -1)}, "")) 101 | } else { 102 | line = append(line, strings.Replace(vars["destination"], "_128", "", -1)) 103 | } 104 | } else { 105 | if (vars["action"] == snatAct) && (r.URL.Query().Get("except") == trueStr) { 106 | line = append(line, strings.Join([]string{"!", strings.Replace(vars["destination"], "_", "/", -1)}, "")) 107 | } else { 108 | line = append(line, strings.Replace(vars["destination"], "_", "/", -1)) 109 | } 110 | } 111 | if r.URL.Query().Get("dport") != "" { 112 | line = append(line, "tcp", strings.Join([]string{"dpt:", r.URL.Query().Get("dport")}, "")) 113 | } 114 | if r.URL.Query().Get("nth_every") != "" { 115 | if r.URL.Query().Get("nth_packet") == "0" { 116 | line = append(line, "statistic", "mode", "nth", "every", r.URL.Query().Get("nth_every")) 117 | } else { 118 | line = append(line, "statistic", "mode", "nth", "every", r.URL.Query().Get("nth_every"), "packet", r.URL.Query().Get("nth_packet")) 119 | } 120 | } 121 | line = append(line, strings.Join([]string{"to:", vars["nat_final"]}, "")) 122 | 123 | ipt, err := iptables.NewWithProtocol(v6) 124 | if err != nil { 125 | return nil, err 126 | } 127 | args := []string{"-t", "nat", "-vnL", vars["chain"], "--line-numbers"} 128 | if ipt.HasWait { 129 | args = append(args, "--wait") 130 | } 131 | nats, err := ipt.ExecuteList(args) 132 | if err != nil { 133 | return nil, err 134 | } 135 | for i := 0; i < len(nats); i++ { 136 | natsSlice := strings.Fields(nats[i]) 137 | natsSliceNoVerb := natsSlice[3:] 138 | if reflect.DeepEqual(line, natsSliceNoVerb) { 139 | linenumber = append(linenumber, natsSlice[0]) 140 | } 141 | } 142 | return linenumber, nil 143 | } 144 | 145 | // PUT /nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 146 | func addNatV6(w http.ResponseWriter, r *http.Request) { 147 | if *htpasswdfile != "" { 148 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 149 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 150 | usercheck := authenticator.CheckAuth(r) 151 | if usercheck == "" { 152 | w.WriteHeader(http.StatusUnauthorized) 153 | return 154 | } 155 | } 156 | vars := mux.Vars(r) 157 | ipt, err := iptables.NewWithProtocol(v6) 158 | if err != nil { 159 | http.Error(w, err.Error(), 500) 160 | return 161 | } 162 | var rulespecs []string 163 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 164 | if r.URL.Query().Get("nth_every") == "" { 165 | w.WriteHeader(http.StatusBadRequest) 166 | fmt.Fprintln(w, "Missing nth every") 167 | return 168 | } 169 | if r.URL.Query().Get("nth_packet") == "" { 170 | w.WriteHeader(http.StatusBadRequest) 171 | fmt.Fprintln(w, "Missing nth packet") 172 | return 173 | } 174 | } 175 | switch vars["action"] { 176 | case dnatAct: 177 | rulespecs = dnatGenerateV6(r) 178 | case snatAct: 179 | rulespecs = snatGenerateV6(r) 180 | default: 181 | w.WriteHeader(http.StatusNotFound) 182 | return 183 | } 184 | if ipt.HasWait { 185 | rulespecs = append(rulespecs, "--wait") 186 | } 187 | if r.URL.Query().Get("position") != "" { 188 | position, err := strconv.Atoi(r.URL.Query().Get("position")) 189 | if err != nil { 190 | http.Error(w, err.Error(), 500) 191 | return 192 | } 193 | respErr = ipt.Insert("nat", vars["chain"], position, rulespecs...) 194 | } else { 195 | respErr = ipt.Append("nat", vars["chain"], rulespecs...) 196 | } 197 | if respErr != nil { 198 | w.WriteHeader(http.StatusBadRequest) 199 | fmt.Fprintln(w, respErr) 200 | } 201 | } 202 | 203 | // DELETE /nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 204 | func delNatV6(w http.ResponseWriter, r *http.Request) { 205 | if *htpasswdfile != "" { 206 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 207 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 208 | usercheck := authenticator.CheckAuth(r) 209 | if usercheck == "" { 210 | w.WriteHeader(http.StatusUnauthorized) 211 | return 212 | } 213 | } 214 | vars := mux.Vars(r) 215 | 216 | ipt, err := iptables.NewWithProtocol(v6) 217 | if err != nil { 218 | http.Error(w, err.Error(), 500) 219 | return 220 | } 221 | var rulespecs []string 222 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 223 | if r.URL.Query().Get("nth_every") == "" { 224 | w.WriteHeader(http.StatusBadRequest) 225 | fmt.Fprintln(w, "Missing nth every") 226 | return 227 | } 228 | if r.URL.Query().Get("nth_packet") == "" { 229 | w.WriteHeader(http.StatusBadRequest) 230 | fmt.Fprintln(w, "Missing nth packet") 231 | return 232 | } 233 | } 234 | switch vars["action"] { 235 | case dnatAct: 236 | rulespecs = dnatGenerateV6(r) 237 | case snatAct: 238 | rulespecs = snatGenerateV6(r) 239 | default: 240 | w.WriteHeader(http.StatusNotFound) 241 | return 242 | } 243 | if ipt.HasWait { 244 | rulespecs = append(rulespecs, "--wait") 245 | } 246 | respErr = ipt.Delete("nat", vars["chain"], rulespecs...) 247 | if respErr != nil { 248 | w.WriteHeader(http.StatusBadRequest) 249 | fmt.Fprintln(w, respErr) 250 | } 251 | } 252 | 253 | // GET /nat_v6/{action}/{chain}/{proto}/{iface}/{source}/{destination}/{nat_final}/?dport=00 254 | func checkNatV6(w http.ResponseWriter, r *http.Request) { 255 | if *htpasswdfile != "" { 256 | htpasswd := auth.HtpasswdFileProvider(*htpasswdfile) 257 | authenticator := auth.NewBasicAuthenticator("Basic Realm", htpasswd) 258 | usercheck := authenticator.CheckAuth(r) 259 | if usercheck == "" { 260 | w.WriteHeader(http.StatusUnauthorized) 261 | return 262 | } 263 | } 264 | vars := mux.Vars(r) 265 | 266 | ipt, err := iptables.NewWithProtocol(v6) 267 | if err != nil { 268 | http.Error(w, err.Error(), 500) 269 | return 270 | } 271 | if r.URL.Query().Get("position") != "" { 272 | posNat, err := checkPosNatV6(r) 273 | 274 | if err != nil { 275 | http.Error(w, err.Error(), 500) 276 | return 277 | } 278 | switch { 279 | case len(posNat) == 0: 280 | w.WriteHeader(http.StatusNotFound) 281 | return 282 | case len(posNat) != 1: 283 | w.WriteHeader(http.StatusConflict) 284 | return 285 | case posNat[0] == r.URL.Query().Get("position"): 286 | return 287 | default: 288 | w.WriteHeader(http.StatusNotFound) 289 | return 290 | } 291 | } 292 | var rulespecs []string 293 | if (r.URL.Query().Get("nth_every") != "") || (r.URL.Query().Get("nth_packet") != "") { 294 | if r.URL.Query().Get("nth_every") == "" { 295 | w.WriteHeader(http.StatusBadRequest) 296 | fmt.Fprintln(w, "Missing nth every") 297 | return 298 | } 299 | if r.URL.Query().Get("nth_packet") == "" { 300 | w.WriteHeader(http.StatusBadRequest) 301 | fmt.Fprintln(w, "Missing nth packet") 302 | return 303 | } 304 | } 305 | switch vars["action"] { 306 | case dnatAct: 307 | rulespecs = dnatGenerateV6(r) 308 | case snatAct: 309 | rulespecs = snatGenerateV6(r) 310 | default: 311 | w.WriteHeader(http.StatusNotFound) 312 | return 313 | } 314 | if ipt.HasWait { 315 | rulespecs = append(rulespecs, "--wait") 316 | } 317 | respStr, respErr := ipt.Exists("nat", vars["chain"], rulespecs...) 318 | if respErr != nil { 319 | w.WriteHeader(http.StatusBadRequest) 320 | fmt.Fprintln(w, respErr) 321 | } 322 | if !respStr { 323 | w.WriteHeader(http.StatusNotFound) 324 | } 325 | } 326 | --------------------------------------------------------------------------------