Skip to content

Commit

Permalink
dpi solver for http using nfqueue
Browse files Browse the repository at this point in the history
  • Loading branch information
orangepizza committed Feb 22, 2023
1 parent 1e2793f commit 476478b
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 0 deletions.
4 changes: 4 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ func CreateFlags(defaultPath string) []cli.Flag {
Name: "http.memcached-host",
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
},
&cli.StringFlag{
Name: "http.nfqueueport",
Usage: "Set the port to use for HTTP based challange. but unlike http it will not bind that port and while other thing already binding that port.",
},
&cli.BoolFlag{
Name: "tls",
Usage: "Use the TLS challenge to solve challenges. Can be mixed with other types of challenges.",
Expand Down
7 changes: 7 additions & 0 deletions cmd/setup_challenges.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/providers/dns"
"github.com/go-acme/lego/v4/providers/http/memcached"
nfqueue "github.com/go-acme/lego/v4/providers/http/nfqueue"
"github.com/go-acme/lego/v4/providers/http/webroot"
"github.com/urfave/cli/v2"
)
Expand Down Expand Up @@ -55,6 +56,12 @@ func setupHTTPProvider(ctx *cli.Context) challenge.Provider {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.nfqueueport"):
ps, err := nfqueue.NewHttpDpiProvider(ctx.String("http.nfqueueport"))
if err != nil {
log.Fatal(err)
}
return ps
case ctx.IsSet("http.port"):
iface := ctx.String("http.port")
if !strings.Contains(iface, ":") {
Expand Down
10 changes: 10 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ require (
software.sslmate.com/src/go-pkcs12 v0.2.0
)

require (
github.com/florianl/go-nfqueue v1.3.1
github.com/google/gopacket v1.1.19
)

require (
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
Expand All @@ -92,18 +97,22 @@ require (
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
github.com/labbsr0x/goh v1.0.1 // indirect
github.com/liquidweb/go-lwApi v0.0.5 // indirect
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.1.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
Expand All @@ -123,6 +132,7 @@ require (
go.opencensus.io v0.22.3 // indirect
go.uber.org/ratelimit v0.2.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
golang.org/x/tools v0.1.12 // indirect
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/florianl/go-nfqueue v1.3.1 h1:khQ9fYCrjbu5CF8dZF55G2RTIEIQRI0Aj5k3msJR6Gw=
github.com/florianl/go-nfqueue v1.3.1/go.mod h1:aHWbgkhryJxF5XxYvJ3oRZpdD4JP74Zu/hP1zuhja+M=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
Expand Down Expand Up @@ -215,13 +217,17 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
Expand Down Expand Up @@ -300,6 +306,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand Down Expand Up @@ -373,6 +381,10 @@ github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
Expand Down Expand Up @@ -675,7 +687,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
Expand All @@ -696,6 +710,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -745,6 +760,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
196 changes: 196 additions & 0 deletions providers/http/nfqueue/nfqueue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
// Package nfqueue implements a HTTP provider for solving the HTTP-01 challenge using nfqueue
// by captureing http challange pacet in fly and answering it by ourself
package nfqueue

import (
"bufio"
"bytes"
"context"
"fmt"
"log"
"net"
"net/http"
"os/exec"
"runtime"
"strings"
"time"

gnfqueue "github.com/florianl/go-nfqueue"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)

// HTTPProvider implements HTTPProvider for `http-01` challenge.
type HTTPProvider struct {
port string
context context.Context
cancel context.CancelFunc
}

// NewHttpDpiProvider returns a HTTPProvider instance with a configured port.
func NewHttpDpiProvider(port string) (*HTTPProvider, error) {

c := &HTTPProvider{
port: port,
}

return c, nil
}

// this craft acme challange response in HTTP level
func craftkeyauthresponse(keyAuth string) []byte {
var reply []byte
reply = fmt.Append(reply, "HTTP/1.1 200 OK\r\n")
reply = fmt.Append(reply, "Content-Type: text/plain\r\n")
reply = fmt.Append(reply, "server: go-acme-nfqueue\r\n")
reply = fmt.Appendf(reply, "Content-Length: %d\r\n", len(keyAuth))
reply = fmt.Append(reply, "\r\n", keyAuth)

return reply
}

// craft packet
func craftReplyPacketBytes(keyAuth string, inputpacket gopacket.Packet) []byte {
outbuffer := gopacket.NewSerializeBuffer()
opt := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
inputTcp := inputpacket.Layer(layers.LayerTypeTCP).(*layers.TCP)
inputIPv4 := inputpacket.Layer(layers.LayerTypeIPv4).(*layers.IPv4)

httplayer := gopacket.Payload(craftkeyauthresponse(keyAuth))
tcplayer := &layers.TCP{
// we reply back so reverse src and dst ports
SrcPort: inputTcp.DstPort,
DstPort: inputTcp.SrcPort,
Ack: inputTcp.Seq + uint32(len(inputTcp.Payload)),
Seq: inputTcp.Ack,
PSH: true,
ACK: true,
}
// log.Infof("dstp: %s, srcp %s", tcplayer.DstPort.String(), tcp)
//check network layer
// this is reply so we reverse sorce and dst ip
iplayer := &layers.IPv4{
SrcIP: inputIPv4.DstIP,
DstIP: inputIPv4.SrcIP,
}
tcplayer.SetNetworkLayerForChecksum(iplayer)
gopacket.SerializeLayers(outbuffer, opt, tcplayer, httplayer)

return outbuffer.Bytes()
}

// sendPacket sends packet: TODO: call cleanup if errors out
func sendPacket(packet []byte, DstIP *net.IP) error {
var err error
con, err := net.Dial("ip:6", DstIP.String())
if err != nil {
return err
}
_, err = con.Write(packet)
if err != nil {
return err
}
return nil
}

// serve runs server by sniffing packets on firewall and inject response into it.
// iptables ://
func (w *HTTPProvider) serve(domain, token, keyAuth string) error {
//run nfqueue start
cmd := exec.Command("iptables", "-I", "INPUT", "-p", "tcp", "--dport", w.port, "-j", "NFQUEUE", "--queue-num", "8555")
err := cmd.Run()
// ensure even if clean funtion failed to called
defer exec.Command("iptables", "-D", "INPUT", "-p", "tcp", "--dport", w.port, "-j", "NFQUEUE", "--queue-num", "8555").Run()
if err != nil {
return err
}
config := gnfqueue.Config{
NfQueue: 8555,
MaxPacketLen: 0xFFFF,
MaxQueueLen: 0xFF,
Copymode: gnfqueue.NfQnlCopyPacket,
WriteTimeout: 15 * time.Millisecond,
}
nf, err := gnfqueue.Open(&config)
if err != nil {
return err
}
defer nf.Close()

//handle Packet
handlepacket := func(a gnfqueue.Attribute) int {
id := *a.PacketID
opt := gopacket.DecodeOptions{
NoCopy: true,
Lazy: false,
}
//assume ipv4 for now, will segfault
payload := gopacket.NewPacket(*a.Payload, layers.LayerTypeIPv4, opt)
ipL := payload.Layer(layers.LayerTypeIPv4)
srcip := ipL.(*layers.IPv4).SrcIP
if tcpLayer := payload.Layer(layers.LayerTypeTCP); tcpLayer != nil {
// Get actual TCP data from this layer
inputTcp, _ := tcpLayer.(*layers.TCP)
// this should be HTTP payload
httpPayload, err := http.ReadRequest(bufio.NewReader((bytes.NewReader(inputTcp.LayerPayload()))))
if err != nil {
nf.SetVerdict(id, gnfqueue.NfAccept)
return 0
}
// check token in http
if strings.Contains(httpPayload.URL.Path, token) {
//we got the token!, block the packet to backend server.
nf.SetVerdict(id, gnfqueue.NfDrop)
//forge our new reply
replypacket := craftReplyPacketBytes(keyAuth, payload)
// Send the modified packet back to VA, ignore err as it won't crash
sendPacket(replypacket, &srcip)
// packet sent, end of function
return 0
} else {
nf.SetVerdict(id, gnfqueue.NfAccept)
return 0
}

} else {
nf.SetVerdict(id, gnfqueue.NfAccept)
}

return 0
}

// Register your function to listen on nflqueue queue
err = nf.Register(w.context, handlepacket)
if err != nil {
fmt.Println(err)
return nil
}

// Block till the context expires
<-w.context.Done()
return nil
}

func (w *HTTPProvider) Present(domain, token, keyAuth string) error {
// test if OS is linux, otherwise no point running this nfqueue is linux thing
if runtime.GOOS != "linux" {
log.Panicf("[%s] http-nfq provider isn't implimented non-linux", domain)
}
w.context, w.cancel = context.WithCancel(context.Background())
go w.serve(domain, token, keyAuth)
return nil
}

// CleanUp removes the firewall rule created for the challenge.
// solve should removed it already but just do be safe:
// iptables -D INPUT -p tcp --dport Port -j NFQUEUE --queue-num 8555
func (w *HTTPProvider) CleanUp(domain, token, keyAuth string) error {
cmd := exec.Command("iptables", "-D", "INPUT", "-p", "tcp", "--dport", w.port, "-j", "NFQUEUE", "--queue-num", "8555")
cmd.Run()
// tell nfqueue to shut down
w.cancel()
return nil
}

0 comments on commit 476478b

Please sign in to comment.