Basics for nftables

Signed-off-by: Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
This commit is contained in:
Georg Pfuetzenreuter 2024-08-30 06:44:37 +02:00
parent b8ca3cec9f
commit f402bce96b
Signed by: Georg
GPG Key ID: 1ED2F138E7E6FF57
5 changed files with 187 additions and 8 deletions

11
go.mod
View File

@ -3,7 +3,18 @@ module nftables-http-api
go 1.22.6 go 1.22.6
require ( require (
github.com/google/nftables v0.2.0
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
golang.org/x/crypto v0.26.0 golang.org/x/crypto v0.26.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require (
github.com/google/go-cmp v0.6.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
golang.org/x/net v0.22.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.23.0 // indirect
)

18
go.sum
View File

@ -1,7 +1,25 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8=
github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

106
nft.go Normal file
View File

@ -0,0 +1,106 @@
/*
* This file is part of nftables-http-api.
* Copyright (C) 2024 Georg Pfuetzenreuter <mail@georg-pfuetzenreuter.net>
*
* The nftables-http-api program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package main
import (
"github.com/google/nftables"
"log"
)
type nftError struct {
Message string
}
func (nfterr nftError) Error() string {
return nfterr.Message
}
func handleNft(task string, set string) (any, error) {
nft, err := nftables.New()
if err != nil {
log.Println("handleNft():", err)
return "", err
}
if task == "get" {
nftResult, err := getNftSetElements(nft, set)
if err == nil {
return nftResult, nil
}
return nil, err
}
return "", nil
}
func getNftTable(nft *nftables.Conn) (*nftables.Table, error) {
targetTable := "filter" // TODO: make table configurable or smarter
foundTables, err := nft.ListTables()
if err != nil {
log.Printf("getNftTable(): %s", err)
return nil, err
}
exists := false
var table *nftables.Table
for _, foundTable := range foundTables {
if foundTable.Name == targetTable {
exists = true
table = foundTable
break
}
}
if !exists {
log.Printf("Table %s does not exist, cannot proceed", targetTable)
return nil, nftError{Message: "Table does not exist"}
}
return table, nil
}
func getNftSet(nft *nftables.Conn, setName string) (*nftables.Set, error) {
foundTable, err := getNftTable(nft)
if err != nil {
return nil, err
}
foundSet, err := nft.GetSetByName(foundTable, setName)
if err != nil || foundSet == nil {
log.Printf("Set lookup for %s failed, cannot proceed: %s", setName, err)
return nil, err
}
return foundSet, nil
}
func getNftSetElements(nft *nftables.Conn, setName string) ([]string, error) {
set, err := getNftSet(nft, setName)
if err != nil {
log.Printf("Could not retrieve set elements")
return nil, err
}
setElements, err := nft.GetSetElements(set)
if err != nil {
return nil, err
}
var returnElements []string
for _, element := range setElements {
log.Printf("element: %s", element.Key)
returnElements = append(returnElements, string(element.Key))
}
return returnElements, nil
}

View File

@ -13,11 +13,12 @@ package main
import ( import (
"flag" "flag"
"github.com/gorilla/mux"
"gopkg.in/yaml.v3"
"log" "log"
"net/http" "net/http"
"os" "os"
"github.com/gorilla/mux" "strings"
"gopkg.in/yaml.v3"
) )
var config Config var config Config
@ -102,6 +103,12 @@ func handleSetRoute(w http.ResponseWriter, r *http.Request) {
log.Printf("Processing authorized %s request from %s for set %s", method, r.RemoteAddr, set) log.Printf("Processing authorized %s request from %s for set %s", method, r.RemoteAddr, set)
if method == "GET" { if method == "GET" {
doReturn(w, http.StatusOK, "ok") nftResult, err := handleNft("get", set)
if err != nil {
doReturn(w, http.StatusInternalServerError, "nftables failure")
}
if nftResult != nil {
doReturn(w, http.StatusOK, strings.Join(nftResult.([]string), ""))
}
} }
} }

View File

@ -13,13 +13,15 @@ package main
import ( import (
"encoding/json" "encoding/json"
"log" "errors"
"net/http"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"log"
"net"
"net/http"
) )
type Response struct { type Response struct {
RError string `json:"error,omitempty"` RError string `json:"error,omitempty"`
RResult string `json:"result,omitempty"` RResult string `json:"result,omitempty"`
} }
@ -47,3 +49,38 @@ func doCheckToken(token string, hash string) bool {
return false return false
} }
} }
func parseIPAddress(straddress string) (net.IP, string) {
parsedaddress := net.ParseIP(straddress)
var address net.IP
family, err := getIPAddressFamily(straddress)
if err == nil {
if family == "ipv4" {
address = parsedaddress.To4()
} else if family == "ipv6" {
address = parsedaddress.To16()
} else {
log.Println("unknown family, this should not happen")
return nil, family
}
return address, family
}
log.Println("address parsing failed:", err)
return nil, ""
}
func getIPAddressFamily(ip string) (string, error) {
if net.ParseIP(ip) == nil {
return "", errors.New("Not an IP address")
}
for i := 0; i < len(ip); i++ {
switch ip[i] {
case '.':
return "ipv4", nil
case ':':
return "ipv6", nil
}
}
return "", errors.New("unknown error")
}