Add wildcard route test and update certgen library

This commit is contained in:
Melon 2023-06-19 16:27:36 +01:00
parent d0149c87dc
commit e9db9d6ef2
Signed by: melon
GPG Key ID: 6C9D970C50D26A25
14 changed files with 133 additions and 101 deletions

View File

@ -1,10 +1,10 @@
package certs
import (
"code.mrmelon54.com/melon/certgen"
"crypto/tls"
"crypto/x509/pkix"
"fmt"
"github.com/MrMelon54/certgen"
"github.com/MrMelon54/violet/utils"
"io/fs"
"log"
@ -36,13 +36,15 @@ func New(certDir fs.FS, keyDir fs.FS, selfCert bool) *Certs {
m: make(map[string]*tls.Certificate),
}
if c.ss {
ca, err := certgen.MakeCaTls(pkix.Name{
ca, err := certgen.MakeCaTls(4096, pkix.Name{
Country: []string{"GB"},
Organization: []string{"Violet"},
OrganizationalUnit: []string{"Development"},
SerialNumber: "0",
CommonName: fmt.Sprintf("%d.violet.test", time.Now().Unix()),
}, big.NewInt(0))
}, big.NewInt(0), func(now time.Time) time.Time {
return now.AddDate(10, 0, 0)
})
if err != nil {
log.Fatalln("Failed to generate CA cert for self-signed mode:", err)
}
@ -67,13 +69,15 @@ func (c *Certs) GetCertForDomain(domain string) *tls.Certificate {
// if self-signed certificate is enabled then generate a certificate
if c.ss {
sn := c.sn.Add(1)
serverTls, err := certgen.MakeServerTls(c.ca, pkix.Name{
serverTls, err := certgen.MakeServerTls(c.ca, 4096, pkix.Name{
Country: []string{"GB"},
Organization: []string{domain},
OrganizationalUnit: []string{domain},
SerialNumber: fmt.Sprintf("%d", sn),
CommonName: domain,
}, big.NewInt(sn), []string{domain}, nil)
}, big.NewInt(sn), func(now time.Time) time.Time {
return now.AddDate(10, 0, 0)
}, []string{domain}, nil)
if err != nil {
return nil
}

View File

@ -1,9 +1,9 @@
package certs
import (
"code.mrmelon54.com/melon/certgen"
"crypto/x509/pkix"
"fmt"
"github.com/MrMelon54/certgen"
"github.com/stretchr/testify/assert"
"math/big"
"testing"
@ -16,24 +16,28 @@ func TestCertsNew_Lookup(t *testing.T) {
// type to test that certificate files can be found and read correctly. This
// uses a MapFS for performance during tests.
ca, err := certgen.MakeCaTls(pkix.Name{
ca, err := certgen.MakeCaTls(4096, pkix.Name{
Country: []string{"GB"},
Organization: []string{"Violet"},
OrganizationalUnit: []string{"Development"},
SerialNumber: "0",
CommonName: fmt.Sprintf("%d.violet.test", time.Now().Unix()),
}, big.NewInt(0))
}, big.NewInt(0), func(now time.Time) time.Time {
return now.AddDate(10, 0, 0)
})
assert.NoError(t, err)
domain := "example.com"
sn := int64(1)
serverTls, err := certgen.MakeServerTls(ca, pkix.Name{
serverTls, err := certgen.MakeServerTls(ca, 4096, pkix.Name{
Country: []string{"GB"},
Organization: []string{domain},
OrganizationalUnit: []string{domain},
SerialNumber: fmt.Sprintf("%d", sn),
CommonName: domain,
}, big.NewInt(sn), []string{domain}, nil)
}, big.NewInt(sn), func(now time.Time) time.Time {
return now.AddDate(10, 0, 0)
}, []string{domain}, nil)
assert.NoError(t, err)
certDir := fstest.MapFS{

View File

@ -5,7 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"fmt"
"github.com/mrmelon54/png2ico"
"github.com/MrMelon54/png2ico"
"image/png"
"io"
"net/http"

9
go.mod
View File

@ -3,16 +3,15 @@ module github.com/MrMelon54/violet
go 1.20
require (
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1
code.mrmelon54.com/melon/summer-utils v0.0.3
github.com/MrMelon54/certgen v0.0.1
github.com/MrMelon54/mjwt v0.0.2
github.com/MrMelon54/png2ico v1.0.1
github.com/MrMelon54/trie v0.0.2
github.com/julienschmidt/httprouter v1.3.0
github.com/mattn/go-sqlite3 v1.14.16
github.com/mrmelon54/mjwt v0.0.1
github.com/mrmelon54/png2ico v1.0.0
github.com/rs/cors v1.9.0
github.com/sethvargo/go-limiter v0.7.2
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
golang.org/x/net v0.9.0
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
)

25
go.sum
View File

@ -1,10 +1,11 @@
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1 h1:tll8DwvO1CL+xXJIMLyDmQYoYr/gA4BkcUFtNHB1BFo=
code.mrmelon54.com/melon/certgen v0.0.0-20220830133534-0fb4cb7e67d1/go.mod h1:Liyhe1bkNyeVfw6LicCgrQ+4oUT/w/qONLjvejkUim0=
code.mrmelon54.com/melon/summer-utils v0.0.3 h1:Bz4o5BBOqWCNGpKkxUum4rwMn/DIdyMCKGQ/D6SXD6Q=
code.mrmelon54.com/melon/summer-utils v0.0.3/go.mod h1:Gh/baXSzkf1ZhHonpPP8oQkyhhmFZcC2yTMlrwclDUw=
github.com/MrMelon54/certgen v0.0.1 h1:ycWdZ2RlxQ5qSuejeBVv4aXjGo5hdqqL4j4EjrXnFMk=
github.com/MrMelon54/certgen v0.0.1/go.mod h1:GHflVlSbtFLJZLpN1oWyUvDBRrR8qCWiwZLXCCnS2Gc=
github.com/MrMelon54/mjwt v0.0.2 h1:jDqyPnFloh80XdSmZ6jt9qhUj/ULcoQ4QSHXPdkAIE4=
github.com/MrMelon54/mjwt v0.0.2/go.mod h1:HzY8P6Je+ovS/fwK5sILRMq5mnZT4+WuFRc98LBy7z4=
github.com/MrMelon54/png2ico v1.0.1 h1:zJoSSl4OkvSIMWGyGPvb8fWNa0KrUvMIjgNGLNLJhVQ=
github.com/MrMelon54/png2ico v1.0.1/go.mod h1:NOv3tO4497mInG+3tcFkIohmxCywUwMLU8WNxJZLVmU=
github.com/MrMelon54/trie v0.0.2 h1:ZXWcX5ij62O9K4I/anuHmVg8L3tF0UGdlPceAASwKEY=
github.com/MrMelon54/trie v0.0.2/go.mod h1:sGCGOcqb+DxSxvHgSOpbpkmA7mFZR47YDExy9OCbVZI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
@ -18,10 +19,6 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mrmelon54/mjwt v0.0.1 h1:XgyWviTmgsbMiKXjxo+Jp/QSf7FF7/omkvrUag8/P5U=
github.com/mrmelon54/mjwt v0.0.1/go.mod h1:M+kZ6t9EArEQ2/CGjfgyNhAo542ot+S7gw5uJCK11Ms=
github.com/mrmelon54/png2ico v1.0.0 h1:YE20i0xao8rkuYaCq3Xj2hUkVkJ6xp412aGDMrGqufA=
github.com/mrmelon54/png2ico v1.0.0/go.mod h1:vp8Be9y5cz102ANon+BnsIzTUdet3VQRvOuWJTH9h0M=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -30,13 +27,8 @@ github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/sethvargo/go-limiter v0.7.2 h1:FgC4N7RMpV5gMrUdda15FaFTkQ/L4fEqM7seXMs4oO8=
github.com/sethvargo/go-limiter v0.7.2/go.mod h1:C0kbSFbiriE5k2FFOe18M1YZbAR2Fiwf72uGu0CXCcU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
@ -46,6 +38,5 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,48 +0,0 @@
package proxy
import (
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"testing"
)
type customTransport struct{}
func (c *customTransport) RoundTrip(_ *http.Request) (*http.Response, error) {
res := httptest.NewRecorder()
res.WriteHeader(http.StatusOK)
res.Write([]byte{0x54, 0x54})
return res.Result(), nil
}
func SetupReverseProxy() *httputil.ReverseProxy {
return &httputil.ReverseProxy{
Director: func(req *http.Request) {},
Transport: &customTransport{},
ModifyResponse: func(rw *http.Response) error { return nil },
ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
log.Printf("[ReverseProxy] Request: %#v\n -- Error: %s\n", req, err)
rw.WriteHeader(http.StatusBadGateway)
_, _ = rw.Write([]byte("502 Bad gateway\n"))
},
}
}
func BenchmarkHttpUtilReverseProxy(b *testing.B) {
rev := SetupReverseProxy()
req, _ := http.NewRequest(http.MethodGet, "https://example.com", nil)
for i := 0; i < b.N; i++ {
rec := httptest.NewRecorder()
rev.ServeHTTP(rec, req)
}
}
func BenchmarkCustomTransport(b *testing.B) {
req, _ := http.NewRequest(http.MethodGet, "https://example.com", nil)
t := &customTransport{}
for i := 0; i < b.N; i++ {
_, _ = t.RoundTrip(req)
}
}

View File

@ -39,7 +39,7 @@ func NewManager(db *sql.DB, proxy *proxy.HybridTransport) *Manager {
m := &Manager{
db: db,
s: &sync.RWMutex{},
r: New(nil),
r: New(proxy),
p: proxy,
}

50
router/manager_test.go Normal file
View File

@ -0,0 +1,50 @@
package router
import (
"database/sql"
"github.com/MrMelon54/violet/proxy"
_ "github.com/mattn/go-sqlite3"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
type fakeTransport struct{ req *http.Request }
func (f *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
f.req = req
rec := httptest.NewRecorder()
rec.WriteHeader(http.StatusOK)
return rec.Result(), nil
}
func TestNewManager(t *testing.T) {
db, err := sql.Open("sqlite3", "file::memory:?cache=shared")
assert.NoError(t, err)
ft := &fakeTransport{}
ht := proxy.NewHybridTransportWithCalls(ft, ft)
m := NewManager(db, ht)
assert.NoError(t, m.internalCompile(m.r))
rec := httptest.NewRecorder()
req, err := http.NewRequest(http.MethodGet, "https://test.example.com", nil)
assert.NoError(t, err)
m.ServeHTTP(rec, req)
res := rec.Result()
assert.Equal(t, http.StatusTeapot, res.StatusCode)
assert.Nil(t, ft.req)
_, err = db.Exec(`INSERT INTO routes (source, pre, destination, abs, cors, secure_mode, forward_host, forward_addr, ignore_cert, active) VALUES (?,?,?,?,?,?,?,?,?,?)`, "*.example.com", 0, "127.0.0.1:8080", 1, 0, 0, 1, 1, 0, 1)
assert.NoError(t, err)
assert.NoError(t, m.internalCompile(m.r))
rec = httptest.NewRecorder()
m.ServeHTTP(rec, req)
res = rec.Result()
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.NotNil(t, ft.req)
}

View File

@ -86,6 +86,8 @@ func (r *Router) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if r.serveRouteHTTP(rw, req, wildcardHost) {
return
}
utils.RespondVioletError(rw, http.StatusTeapot, "No route")
}
func (r *Router) serveRouteHTTP(rw http.ResponseWriter, req *http.Request, host string) bool {

View File

@ -174,15 +174,6 @@ var (
}
)
type fakeTransport struct{ req *http.Request }
func (f *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
f.req = req
rec := httptest.NewRecorder()
rec.WriteHeader(http.StatusOK)
return rec.Result(), nil
}
func TestRouter_AddRoute(t *testing.T) {
transSecure := &fakeTransport{}
transInsecure := &fakeTransport{}
@ -267,3 +258,42 @@ func outputUrl(u *url.URL) string {
}
return u.String()
}
func TestRouter_AddWildcardRoute(t *testing.T) {
transSecure := &fakeTransport{}
transInsecure := &fakeTransport{}
for _, i := range routeTests {
r := New(proxy.NewHybridTransportWithCalls(transSecure, transInsecure))
dst := i.dst
dst.Host = "127.0.0.1"
dst.Port = 8080
t.Logf("Running tests for %#v\n", dst)
r.AddRoute("example.com", i.path, dst)
for k, v := range i.tests {
u1 := &url.URL{Scheme: "https", Host: "example.com", Path: k}
req, _ := http.NewRequest(http.MethodGet, u1.String(), nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
if v == "" {
if transSecure.req != nil {
t.Logf("Test URL: %#v\n", req.URL)
t.Log(r.redirect["example.com"].String())
t.Fatalf("%s => %s\n", k, v)
}
} else {
if transSecure.req == nil {
t.Logf("Test URL: %#v\n", req.URL)
t.Log(r.route["example.com"].String())
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, "")
}
if v != transSecure.req.URL.Path {
t.Logf("Test URL: %#v\n", req.URL)
t.Log(r.route["example.com"].String())
t.Fatalf("\nexpected %s => %s\n got %s => %s\n", k, v, k, transSecure.req.URL.Path)
}
transSecure.req = nil
}
}
}
}

View File

@ -1,10 +1,10 @@
package servers
import (
"code.mrmelon54.com/melon/summer-utils/claims/auth"
"github.com/MrMelon54/mjwt"
"github.com/MrMelon54/mjwt/auth"
"github.com/MrMelon54/violet/utils"
"github.com/julienschmidt/httprouter"
"github.com/mrmelon54/mjwt"
"net/http"
"time"
)
@ -68,7 +68,7 @@ func NewApiServer(conf *Conf, compileTarget utils.MultiCompilable) *http.Server
}
}
func hasPerms(verify mjwt.Provider, req *http.Request, perm string) bool {
func hasPerms(verify mjwt.Verifier, req *http.Request, perm string) bool {
// Get bearer token
bearer := utils.GetBearer(req)
if bearer == "" {

View File

@ -1,12 +1,12 @@
package servers
import (
"code.mrmelon54.com/melon/summer-utils/claims"
"code.mrmelon54.com/melon/summer-utils/claims/auth"
"crypto/rand"
"crypto/rsa"
"github.com/MrMelon54/mjwt"
"github.com/MrMelon54/mjwt/auth"
"github.com/MrMelon54/mjwt/claims"
"github.com/MrMelon54/violet/utils"
"github.com/mrmelon54/mjwt"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
@ -20,7 +20,7 @@ type fakeDomains struct{}
func (f *fakeDomains) IsValid(host string) bool { return host == "example.com" }
func genSnakeOilProv() mjwt.Provider {
func genSnakeOilProv() mjwt.Signer {
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
panic(err)

View File

@ -3,10 +3,10 @@ package servers
import (
"crypto/tls"
"database/sql"
"github.com/MrMelon54/mjwt"
errorPages "github.com/MrMelon54/violet/error-pages"
"github.com/MrMelon54/violet/favicons"
"github.com/MrMelon54/violet/router"
"github.com/mrmelon54/mjwt"
)
// Conf stores the shared configuration for the API, HTTP and HTTPS servers.
@ -20,7 +20,7 @@ type Conf struct {
Acme AcmeChallengeProvider
Certs CertProvider
Favicons *favicons.Favicons
Verify mjwt.Provider
Verify mjwt.Verifier
ErrorPages *errorPages.ErrorPages
Router *router.Manager
}

View File

@ -47,7 +47,7 @@ func TestNewHttpsServer_RateLimit(t *testing.T) {
rec := httptest.NewRecorder()
srv.Handler.ServeHTTP(rec, req)
res := rec.Result()
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, http.StatusTeapot, res.StatusCode)
}()
}
wg.Wait()