mirror of
https://github.com/TecharoHQ/anubis.git
synced 2026-04-17 05:44:57 +00:00
test(osiris): test osiris features and ensure it works
Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
@@ -16,10 +16,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Domain struct {
|
type Domain struct {
|
||||||
Name string `hcl:"name,label"`
|
Name string `hcl:"name,label"`
|
||||||
TLS TLS `hcl:"tls,block"`
|
TLS TLS `hcl:"tls,block"`
|
||||||
Target string `hcl:"target"`
|
Target string `hcl:"target"`
|
||||||
HealthTarget string `hcl:"health_target"`
|
InsecureSkipVerify bool `hcl:"insecure_skip_verify,optional"`
|
||||||
|
HealthTarget string `hcl:"health_target"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d Domain) Valid() error {
|
func (d Domain) Valid() error {
|
||||||
|
|||||||
@@ -2,20 +2,13 @@ package entrypoint
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
||||||
"github.com/TecharoHQ/anubis/internal"
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/internal/fingerprint"
|
|
||||||
"github.com/hashicorp/hcl/v2/hclsimple"
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
)
|
)
|
||||||
@@ -24,12 +17,9 @@ type Options struct {
|
|||||||
ConfigFname string
|
ConfigFname string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Main(opts Options) error {
|
func Main(ctx context.Context, opts Options) error {
|
||||||
internal.SetHealth("osiris", healthv1.HealthCheckResponse_NOT_SERVING)
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_NOT_SERVING)
|
||||||
|
|
||||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
var cfg config.Toplevel
|
var cfg config.Toplevel
|
||||||
if err := hclsimple.DecodeFile(opts.ConfigFname, nil, &cfg); err != nil {
|
if err := hclsimple.DecodeFile(opts.ConfigFname, nil, &cfg); err != nil {
|
||||||
return fmt.Errorf("can't read configuration file %s:\n\n%w", opts.ConfigFname, err)
|
return fmt.Errorf("can't read configuration file %s:\n\n%w", opts.ConfigFname, err)
|
||||||
@@ -57,13 +47,11 @@ func Main(opts Options) error {
|
|||||||
go func(ctx context.Context) {
|
go func(ctx context.Context) {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
ln.Close()
|
ln.Close()
|
||||||
}(gCtx)
|
}(ctx)
|
||||||
|
|
||||||
slog.Info("listening", "for", "http", "bind", cfg.Bind.HTTP)
|
slog.Info("listening", "for", "http", "bind", cfg.Bind.HTTP)
|
||||||
|
|
||||||
srv := http.Server{Handler: rtr, ErrorLog: internal.GetFilteredHTTPLogger()}
|
return rtr.HandleHTTP(gCtx, ln)
|
||||||
|
|
||||||
return srv.Serve(ln)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// HTTPS
|
// HTTPS
|
||||||
@@ -77,70 +65,16 @@ func Main(opts Options) error {
|
|||||||
go func(ctx context.Context) {
|
go func(ctx context.Context) {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
ln.Close()
|
ln.Close()
|
||||||
}(gCtx)
|
}(ctx)
|
||||||
|
|
||||||
tc := &tls.Config{
|
|
||||||
GetCertificate: rtr.GetCertificate,
|
|
||||||
}
|
|
||||||
|
|
||||||
srv := &http.Server{
|
|
||||||
Addr: cfg.Bind.HTTPS,
|
|
||||||
Handler: rtr,
|
|
||||||
ErrorLog: internal.GetFilteredHTTPLogger(),
|
|
||||||
TLSConfig: tc,
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprint.ApplyTLSFingerprinter(srv)
|
|
||||||
|
|
||||||
slog.Info("listening", "for", "https", "bind", cfg.Bind.HTTPS)
|
slog.Info("listening", "for", "https", "bind", cfg.Bind.HTTPS)
|
||||||
|
|
||||||
return srv.ServeTLS(ln, "", "")
|
return rtr.HandleHTTPS(gCtx, ln)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
ln, err := net.Listen("tcp", cfg.Bind.Metrics)
|
return rtr.ListenAndServeMetrics(gCtx, cfg.Bind.Metrics)
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("(metrics) can't bind to tcp %s: %w", cfg.Bind.Metrics, err)
|
|
||||||
}
|
|
||||||
defer ln.Close()
|
|
||||||
|
|
||||||
go func(ctx context.Context) {
|
|
||||||
<-ctx.Done()
|
|
||||||
ln.Close()
|
|
||||||
}(gCtx)
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
|
|
||||||
mux.Handle("/metrics", promhttp.Handler())
|
|
||||||
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
st, ok := internal.GetHealth("osiris")
|
|
||||||
if !ok {
|
|
||||||
slog.Error("health service osiris does not exist, file a bug")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch st {
|
|
||||||
case healthv1.HealthCheckResponse_NOT_SERVING:
|
|
||||||
http.Error(w, "NOT OK", http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
case healthv1.HealthCheckResponse_SERVING:
|
|
||||||
fmt.Fprintln(w, "OK")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
http.Error(w, "UNKNOWN", http.StatusFailedDependency)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
slog.Info("listening", "for", "metrics", "bind", cfg.Bind.Metrics)
|
|
||||||
|
|
||||||
srv := http.Server{
|
|
||||||
Addr: cfg.Bind.Metrics,
|
|
||||||
Handler: mux,
|
|
||||||
ErrorLog: internal.GetFilteredHTTPLogger(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return srv.Serve(ln)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
internal.SetHealth("osiris", healthv1.HealthCheckResponse_SERVING)
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_SERVING)
|
||||||
|
|||||||
93
cmd/osiris/internal/entrypoint/entrypoint_test.go
Normal file
93
cmd/osiris/internal/entrypoint/entrypoint_test.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package entrypoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMainGoodConfig(t *testing.T) {
|
||||||
|
files, err := os.ReadDir("./testdata/good")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range files {
|
||||||
|
t.Run(st.Name(), func(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
|
cfg := loadConfig(t, filepath.Join("testdata", "good", st.Name()))
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
if err := Main(ctx, Options{
|
||||||
|
ConfigFname: filepath.Join("testdata", "good", st.Name()),
|
||||||
|
}); err != nil {
|
||||||
|
var netOpErr *net.OpError
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, context.Canceled):
|
||||||
|
// Context was canceled, this is expected
|
||||||
|
return
|
||||||
|
case errors.As(err, &netOpErr):
|
||||||
|
// Network operation error occurred
|
||||||
|
t.Logf("Network operation error: %v", netOpErr)
|
||||||
|
return
|
||||||
|
case errors.Is(err, http.ErrServerClosed):
|
||||||
|
// Server was closed, this is expected
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// Other unexpected error
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(ctx)
|
||||||
|
|
||||||
|
wait := 5 * time.Millisecond
|
||||||
|
|
||||||
|
for i := range make([]struct{}, 10) {
|
||||||
|
if i != 0 {
|
||||||
|
time.Sleep(wait)
|
||||||
|
wait = wait * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("try %d (wait=%s)", i+1, wait)
|
||||||
|
|
||||||
|
resp, err := http.Get("http://localhost" + cfg.Bind.Metrics + "/readyz")
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("router initialization did not work")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMainBadConfig(t *testing.T) {
|
||||||
|
files, err := os.ReadDir("./testdata/bad")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, st := range files {
|
||||||
|
t.Run(st.Name(), func(t *testing.T) {
|
||||||
|
if err := Main(t.Context(), Options{
|
||||||
|
ConfigFname: filepath.Join("testdata", "bad", st.Name()),
|
||||||
|
}); err == nil {
|
||||||
|
t.Error("wanted an error but got none")
|
||||||
|
} else {
|
||||||
|
t.Log(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func newH2CReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
func newH2CReverseProxy(target *url.URL) *httputil.ReverseProxy {
|
||||||
|
target.Scheme = "http"
|
||||||
|
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
req.URL.Scheme = target.Scheme
|
req.URL.Scheme = target.Scheme
|
||||||
req.URL.Host = target.Host
|
req.URL.Host = target.Host
|
||||||
|
|||||||
51
cmd/osiris/internal/entrypoint/h2c_test.go
Normal file
51
cmd/osiris/internal/entrypoint/h2c_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package entrypoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
"golang.org/x/net/http2/h2c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newH2cServer(t *testing.T, h http.Handler) *httptest.Server {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
h2s := &http2.Server{}
|
||||||
|
|
||||||
|
srv := httptest.NewServer(h2c.NewHandler(h, h2s))
|
||||||
|
t.Cleanup(func() {
|
||||||
|
srv.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
return srv
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestH2CReverseProxy(t *testing.T) {
|
||||||
|
h := &ackHandler{}
|
||||||
|
|
||||||
|
srv := newH2cServer(t, h)
|
||||||
|
|
||||||
|
u, err := url.Parse(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rp := httptest.NewServer(newH2CReverseProxy(u))
|
||||||
|
defer rp.Close()
|
||||||
|
|
||||||
|
resp, err := rp.Client().Get(rp.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("wrong status code from reverse proxy: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.ack {
|
||||||
|
t.Error("h2c handler was not executed")
|
||||||
|
}
|
||||||
|
}
|
||||||
72
cmd/osiris/internal/entrypoint/metrics.go
Normal file
72
cmd/osiris/internal/entrypoint/metrics.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package entrypoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
|
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func healthz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
services, err := internal.HealthSrv.List(r.Context(), nil)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("can't get list of services", "err", err)
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys []string
|
||||||
|
for k := range services.Statuses {
|
||||||
|
if k == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
var msg bytes.Buffer
|
||||||
|
|
||||||
|
var healthy bool = true
|
||||||
|
|
||||||
|
for _, k := range keys {
|
||||||
|
st := services.Statuses[k].GetStatus()
|
||||||
|
fmt.Fprintf(&msg, "%s: %s\n", k, st)
|
||||||
|
switch st {
|
||||||
|
case healthv1.HealthCheckResponse_SERVING:
|
||||||
|
// do nothing
|
||||||
|
default:
|
||||||
|
healthy = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !healthy {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(msg.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func readyz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
st, ok := internal.GetHealth("osiris")
|
||||||
|
if !ok {
|
||||||
|
slog.Error("health service osiris does not exist, file a bug")
|
||||||
|
http.Error(w, "health service osiris does not exist", http.StatusExpectationFailed)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch st {
|
||||||
|
case healthv1.HealthCheckResponse_NOT_SERVING:
|
||||||
|
http.Error(w, "NOT OK", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
case healthv1.HealthCheckResponse_SERVING:
|
||||||
|
fmt.Fprintln(w, "OK")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
http.Error(w, "UNKNOWN", http.StatusFailedDependency)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
66
cmd/osiris/internal/entrypoint/metrics_test.go
Normal file
66
cmd/osiris/internal/entrypoint/metrics_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package entrypoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
|
healthv1 "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHealthz(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(healthz))
|
||||||
|
|
||||||
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_NOT_SERVING)
|
||||||
|
|
||||||
|
resp, err := srv.Client().Get(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
t.Errorf("wanted not ready but got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_SERVING)
|
||||||
|
|
||||||
|
resp, err = srv.Client().Get(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("wanted ready but got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadyz(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(readyz))
|
||||||
|
|
||||||
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_NOT_SERVING)
|
||||||
|
|
||||||
|
resp, err := srv.Client().Get(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
t.Errorf("wanted not ready but got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal.SetHealth("osiris", healthv1.HealthCheckResponse_SERVING)
|
||||||
|
|
||||||
|
resp, err = srv.Client().Get(srv.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Errorf("wanted ready but got %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,10 +14,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
||||||
|
"github.com/TecharoHQ/anubis/internal"
|
||||||
"github.com/TecharoHQ/anubis/internal/fingerprint"
|
"github.com/TecharoHQ/anubis/internal/fingerprint"
|
||||||
|
"github.com/felixge/httpsnoop"
|
||||||
"github.com/lum8rjack/go-ja4h"
|
"github.com/lum8rjack/go-ja4h"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -30,6 +33,12 @@ var (
|
|||||||
Namespace: "techaro",
|
Namespace: "techaro",
|
||||||
Subsystem: "osiris",
|
Subsystem: "osiris",
|
||||||
Name: "request_count",
|
Name: "request_count",
|
||||||
|
}, []string{"domain", "method", "response_code"})
|
||||||
|
|
||||||
|
responseTime = promauto.NewHistogramVec(prometheus.HistogramOpts{
|
||||||
|
Namespace: "techaro",
|
||||||
|
Subsystem: "osiris",
|
||||||
|
Name: "response_time",
|
||||||
}, []string{"domain"})
|
}, []string{"domain"})
|
||||||
|
|
||||||
unresolvedRequests = promauto.NewGauge(prometheus.GaugeOpts{
|
unresolvedRequests = promauto.NewGauge(prometheus.GaugeOpts{
|
||||||
@@ -60,18 +69,35 @@ func (rtr *Router) setConfig(c config.Toplevel) error {
|
|||||||
|
|
||||||
var h http.Handler
|
var h http.Handler
|
||||||
|
|
||||||
switch u.Scheme {
|
if u != nil {
|
||||||
case "http", "https":
|
switch u.Scheme {
|
||||||
h = httputil.NewSingleHostReverseProxy(u)
|
case "http", "https":
|
||||||
case "h2c":
|
rp := httputil.NewSingleHostReverseProxy(u)
|
||||||
h = newH2CReverseProxy(u)
|
|
||||||
case "unix":
|
if d.InsecureSkipVerify {
|
||||||
h = &httputil.ReverseProxy{
|
rp.Transport = &http.Transport{
|
||||||
Transport: &http.Transport{
|
TLSClientConfig: &tls.Config{
|
||||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
InsecureSkipVerify: true,
|
||||||
return net.Dial("unix", strings.TrimPrefix(d.Target, "unix://"))
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h = rp
|
||||||
|
case "h2c":
|
||||||
|
h = newH2CReverseProxy(u)
|
||||||
|
case "unix":
|
||||||
|
h = &httputil.ReverseProxy{
|
||||||
|
Director: func(r *http.Request) {
|
||||||
|
r.URL.Scheme = "http"
|
||||||
|
r.URL.Host = d.Name
|
||||||
|
r.Host = d.Name
|
||||||
},
|
},
|
||||||
},
|
Transport: &http.Transport{
|
||||||
|
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial("unix", strings.TrimPrefix(d.Target, "unix://"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +155,75 @@ func NewRouter(c config.Toplevel) (*Router, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rtr *Router) HandleHTTP(ctx context.Context, ln net.Listener) error {
|
||||||
|
srv := http.Server{
|
||||||
|
Handler: rtr,
|
||||||
|
ErrorLog: internal.GetFilteredHTTPLogger(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Close()
|
||||||
|
}(ctx)
|
||||||
|
|
||||||
|
return srv.Serve(ln)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtr *Router) HandleHTTPS(ctx context.Context, ln net.Listener) error {
|
||||||
|
tc := &tls.Config{
|
||||||
|
GetCertificate: rtr.GetCertificate,
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Handler: rtr,
|
||||||
|
ErrorLog: internal.GetFilteredHTTPLogger(),
|
||||||
|
TLSConfig: tc,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Close()
|
||||||
|
}(ctx)
|
||||||
|
|
||||||
|
fingerprint.ApplyTLSFingerprinter(srv)
|
||||||
|
|
||||||
|
return srv.ServeTLS(ln, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rtr *Router) ListenAndServeMetrics(ctx context.Context, addr string) error {
|
||||||
|
ln, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("(metrics) can't bind to tcp %s: %w", addr, err)
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
<-ctx.Done()
|
||||||
|
ln.Close()
|
||||||
|
}(ctx)
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.Handle("/metrics", promhttp.Handler())
|
||||||
|
mux.HandleFunc("/readyz", readyz)
|
||||||
|
mux.HandleFunc("/healthz", healthz)
|
||||||
|
|
||||||
|
slog.Info("listening", "for", "metrics", "bind", addr)
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
Handler: mux,
|
||||||
|
ErrorLog: internal.GetFilteredHTTPLogger(),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Close()
|
||||||
|
}(ctx)
|
||||||
|
|
||||||
|
return srv.Serve(ln)
|
||||||
|
}
|
||||||
|
|
||||||
func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
var host = r.Host
|
var host = r.Host
|
||||||
|
|
||||||
@@ -136,8 +231,6 @@ func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
host, _, _ = net.SplitHostPort(host)
|
host, _, _ = net.SplitHostPort(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestsPerDomain.WithLabelValues(host).Inc()
|
|
||||||
|
|
||||||
var h http.Handler
|
var h http.Handler
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
@@ -170,5 +263,10 @@ func (rtr *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
r.Header.Set("X-Tcp-Ja4t-Fingerprint", tcpFP.String())
|
r.Header.Set("X-Tcp-Ja4t-Fingerprint", tcpFP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
h.ServeHTTP(w, r)
|
m := httpsnoop.CaptureMetrics(h, w, r)
|
||||||
|
|
||||||
|
requestsPerDomain.WithLabelValues(host, r.Method, fmt.Sprint(m.Code)).Inc()
|
||||||
|
responseTime.WithLabelValues(host).Observe(float64(m.Duration.Milliseconds()))
|
||||||
|
|
||||||
|
slog.Info("request completed", "host", host, "method", r.Method, "response_code", m.Code, "duration_ms", m.Duration.Milliseconds())
|
||||||
}
|
}
|
||||||
|
|||||||
319
cmd/osiris/internal/entrypoint/router_test.go
Normal file
319
cmd/osiris/internal/entrypoint/router_test.go
Normal file
@@ -0,0 +1,319 @@
|
|||||||
|
package entrypoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/TecharoHQ/anubis/cmd/osiris/internal/config"
|
||||||
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadConfig(t *testing.T, fname string) config.Toplevel {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
var cfg config.Toplevel
|
||||||
|
if err := hclsimple.DecodeFile(fname, nil, &cfg); err != nil {
|
||||||
|
t.Fatalf("can't read configuration file %s: %v", fname, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.Valid(); err != nil {
|
||||||
|
t.Errorf("configuration file %s is invalid: %v", "./testdata/selfsigned.hcl", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRouter(t *testing.T, cfg config.Toplevel) *Router {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
rtr, err := NewRouter(cfg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtr
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRouter(t *testing.T) {
|
||||||
|
cfg := loadConfig(t, "./testdata/good/selfsigned.hcl")
|
||||||
|
rtr := newRouter(t, cfg)
|
||||||
|
|
||||||
|
srv := httptest.NewServer(rtr)
|
||||||
|
defer srv.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewRouterFails(t *testing.T) {
|
||||||
|
cfg := loadConfig(t, "./testdata/good/selfsigned.hcl")
|
||||||
|
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "test1.internal",
|
||||||
|
TLS: config.TLS{
|
||||||
|
Cert: "./testdata/tls/invalid.crt",
|
||||||
|
Key: "./testdata/tls/invalid.key",
|
||||||
|
},
|
||||||
|
Target: cfg.Domains[0].Target,
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
rtr, err := NewRouter(cfg)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("wanted an error but got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := httptest.NewServer(rtr)
|
||||||
|
defer srv.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterSetConfig(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
configFname string
|
||||||
|
mutation func(cfg config.Toplevel) config.Toplevel
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "basic",
|
||||||
|
configFname: "./testdata/good/selfsigned.hcl",
|
||||||
|
mutation: func(cfg config.Toplevel) config.Toplevel {
|
||||||
|
return cfg
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "all schemes",
|
||||||
|
configFname: "./testdata/good/selfsigned.hcl",
|
||||||
|
mutation: func(cfg config.Toplevel) config.Toplevel {
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "http.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "http://[::1]:3000",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "https.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "https://[::1]:3000",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "h2c.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "h2c://[::1]:3000",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "unix.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "unix://foo.sock",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid TLS",
|
||||||
|
configFname: "./testdata/good/selfsigned.hcl",
|
||||||
|
mutation: func(cfg config.Toplevel) config.Toplevel {
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "test1.internal",
|
||||||
|
TLS: config.TLS{
|
||||||
|
Cert: "./testdata/tls/invalid.crt",
|
||||||
|
Key: "./testdata/tls/invalid.key",
|
||||||
|
},
|
||||||
|
Target: cfg.Domains[0].Target,
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
},
|
||||||
|
err: ErrInvalidTLSKeypair,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "target is not a valid URL",
|
||||||
|
configFname: "./testdata/good/selfsigned.hcl",
|
||||||
|
mutation: func(cfg config.Toplevel) config.Toplevel {
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "test1.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "http://[::1:443",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
},
|
||||||
|
err: ErrTargetInvalid,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid target scheme",
|
||||||
|
configFname: "./testdata/good/selfsigned.hcl",
|
||||||
|
mutation: func(cfg config.Toplevel) config.Toplevel {
|
||||||
|
cfg.Domains = append(cfg.Domains, config.Domain{
|
||||||
|
Name: "test1.internal",
|
||||||
|
TLS: cfg.Domains[0].TLS,
|
||||||
|
Target: "foo://",
|
||||||
|
HealthTarget: cfg.Domains[0].HealthTarget,
|
||||||
|
})
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
},
|
||||||
|
err: ErrNoHandler,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
cfg := loadConfig(t, tt.configFname)
|
||||||
|
rtr := newRouter(t, cfg)
|
||||||
|
|
||||||
|
cfg = tt.mutation(cfg)
|
||||||
|
|
||||||
|
if err := rtr.setConfig(cfg); !errors.Is(err, tt.err) {
|
||||||
|
t.Logf("want: %v", tt.err)
|
||||||
|
t.Logf("got: %v", err)
|
||||||
|
t.Error("got wrong error from rtr.setConfig function")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ackHandler struct {
|
||||||
|
ack bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *ackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ah.ack = true
|
||||||
|
fmt.Fprintln(w, "OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *ackHandler) Reset() {
|
||||||
|
ah.ack = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUnixServer(t *testing.T, h http.Handler) string {
|
||||||
|
sockName := filepath.Join(t.TempDir(), "s")
|
||||||
|
ln, err := net.Listen("unix", sockName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't listen on %s: %v", sockName, err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
ln.Close()
|
||||||
|
os.Remove(sockName)
|
||||||
|
})
|
||||||
|
|
||||||
|
go func(ctx context.Context) {
|
||||||
|
srv := &http.Server{
|
||||||
|
Handler: h,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
srv.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
srv.Serve(ln)
|
||||||
|
}(t.Context())
|
||||||
|
|
||||||
|
return "unix://" + sockName
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterGetCertificate(t *testing.T) {
|
||||||
|
cfg := loadConfig(t, "./testdata/good/selfsigned.hcl")
|
||||||
|
rtr := newRouter(t, cfg)
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
domainName string
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
domainName: "osiris.local.cetacean.club",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
domainName: "whacky-fun.local",
|
||||||
|
err: ErrNoCert,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tt.domainName, func(t *testing.T) {
|
||||||
|
if _, err := rtr.GetCertificate(&tls.ClientHelloInfo{ServerName: tt.domainName}); !errors.Is(err, tt.err) {
|
||||||
|
t.Logf("want: %v", tt.err)
|
||||||
|
t.Logf("got: %v", err)
|
||||||
|
t.Error("got wrong error from rtr.GetCertificate")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterServeAllProtocols(t *testing.T) {
|
||||||
|
cfg := loadConfig(t, "./testdata/good/all_protocols.hcl")
|
||||||
|
|
||||||
|
httpAckHandler := &ackHandler{}
|
||||||
|
httpsAckHandler := &ackHandler{}
|
||||||
|
h2cAckHandler := &ackHandler{}
|
||||||
|
unixAckHandler := &ackHandler{}
|
||||||
|
|
||||||
|
httpSrv := httptest.NewServer(httpAckHandler)
|
||||||
|
httpsSrv := httptest.NewTLSServer(httpsAckHandler)
|
||||||
|
h2cSrv := newH2cServer(t, h2cAckHandler)
|
||||||
|
unixPath := newUnixServer(t, unixAckHandler)
|
||||||
|
|
||||||
|
cfg.Domains[0].Target = httpSrv.URL
|
||||||
|
cfg.Domains[1].Target = httpsSrv.URL
|
||||||
|
cfg.Domains[2].Target = strings.ReplaceAll(h2cSrv.URL, "http:", "h2c:")
|
||||||
|
cfg.Domains[3].Target = unixPath
|
||||||
|
|
||||||
|
// enc := json.NewEncoder(os.Stderr)
|
||||||
|
// enc.SetIndent("", " ")
|
||||||
|
// enc.Encode(cfg)
|
||||||
|
|
||||||
|
rtr := newRouter(t, cfg)
|
||||||
|
|
||||||
|
cli := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("plain http", func(t *testing.T) {
|
||||||
|
ln, err := net.Listen("tcp", ":0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
ln.Close()
|
||||||
|
})
|
||||||
|
|
||||||
|
go rtr.HandleHTTP(t.Context(), ln)
|
||||||
|
|
||||||
|
serverURL := "http://" + ln.Addr().String()
|
||||||
|
t.Log(serverURL)
|
||||||
|
|
||||||
|
for _, d := range cfg.Domains {
|
||||||
|
t.Run(d.Name, func(t *testing.T) {
|
||||||
|
req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, serverURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Host = d.Name
|
||||||
|
|
||||||
|
resp, err := cli.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
t.Fatalf("wrong status code %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
0
cmd/osiris/internal/entrypoint/testdata/bad/empty.hcl
vendored
Normal file
0
cmd/osiris/internal/entrypoint/testdata/bad/empty.hcl
vendored
Normal file
15
cmd/osiris/internal/entrypoint/testdata/bad/invalid.hcl
vendored
Normal file
15
cmd/osiris/internal/entrypoint/testdata/bad/invalid.hcl
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
bind {
|
||||||
|
http = ":65530"
|
||||||
|
https = ":65531"
|
||||||
|
metrics = ":65532"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "osiris.local.cetacean.club" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/invalid.crt"
|
||||||
|
key = "./testdata/invalid.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "http://localhost:3000"
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
46
cmd/osiris/internal/entrypoint/testdata/good/all_protocols.hcl
vendored
Normal file
46
cmd/osiris/internal/entrypoint/testdata/good/all_protocols.hcl
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
bind {
|
||||||
|
http = ":65520"
|
||||||
|
https = ":65521"
|
||||||
|
metrics = ":65522"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "http.internal" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/selfsigned.crt"
|
||||||
|
key = "./testdata/selfsigned.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "http://localhost:65510" # XXX(Xe) this is overwritten
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "https.internal" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/selfsigned.crt"
|
||||||
|
key = "./testdata/selfsigned.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "https://localhost:65511" # XXX(Xe) this is overwritten
|
||||||
|
insecure_skip_verify = true
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "h2c.internal" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/selfsigned.crt"
|
||||||
|
key = "./testdata/selfsigned.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "h2c://localhost:65511" # XXX(Xe) this is overwritten
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "unix.internal" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/selfsigned.crt"
|
||||||
|
key = "./testdata/selfsigned.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "http://localhost:65511" # XXX(Xe) this is overwritten
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
15
cmd/osiris/internal/entrypoint/testdata/good/selfsigned.hcl
vendored
Normal file
15
cmd/osiris/internal/entrypoint/testdata/good/selfsigned.hcl
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
bind {
|
||||||
|
http = ":65530"
|
||||||
|
https = ":65531"
|
||||||
|
metrics = ":65532"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain "osiris.local.cetacean.club" {
|
||||||
|
tls {
|
||||||
|
cert = "./testdata/selfsigned.crt"
|
||||||
|
key = "./testdata/selfsigned.key"
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "http://localhost:3000"
|
||||||
|
health_target = "http://localhost:9091/healthz"
|
||||||
|
}
|
||||||
11
cmd/osiris/internal/entrypoint/testdata/selfsigned.crt
vendored
Normal file
11
cmd/osiris/internal/entrypoint/testdata/selfsigned.crt
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIBnzCCAVGgAwIBAgIUOLTjSYOjFk00IemtFTC4oEZs988wBQYDK2VwMEUxCzAJ
|
||||||
|
BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l
|
||||||
|
dCBXaWRnaXRzIFB0eSBMdGQwHhcNMjUwNzE4MjEyNDIzWhcNMjUwODE3MjEyNDIz
|
||||||
|
WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwY
|
||||||
|
SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMCowBQYDK2VwAyEAPHphABS15+4VV6R1
|
||||||
|
vYzBQYIycQmOmlbA8QcfwzuB2VajUzBRMB0GA1UdDgQWBBT2s+MQ4AR6cbK4V0+d
|
||||||
|
XZnok1orhDAfBgNVHSMEGDAWgBT2s+MQ4AR6cbK4V0+dXZnok1orhDAPBgNVHRMB
|
||||||
|
Af8EBTADAQH/MAUGAytlcANBAOdoJbRMnHmkEETzVtXP+jkAI9yQNRXujnglApGP
|
||||||
|
8I5pvIYVgYCgoQrnb4haVWFldHM1T9H698n19e/egfFb+w4=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
3
cmd/osiris/internal/entrypoint/testdata/selfsigned.key
vendored
Normal file
3
cmd/osiris/internal/entrypoint/testdata/selfsigned.key
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MC4CAQAwBQYDK2VwBCIEIBop42tiZ0yzhaKo9NAc0PlAyBsE8NAE0i9Z7s2lgZuR
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/TecharoHQ/anubis"
|
"github.com/TecharoHQ/anubis"
|
||||||
"github.com/TecharoHQ/anubis/cmd/osiris/internal/entrypoint"
|
"github.com/TecharoHQ/anubis/cmd/osiris/internal/entrypoint"
|
||||||
@@ -28,7 +31,10 @@ func main() {
|
|||||||
|
|
||||||
internal.InitSlog(*slogLevel)
|
internal.InitSlog(*slogLevel)
|
||||||
|
|
||||||
if err := entrypoint.Main(entrypoint.Options{
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := entrypoint.Main(ctx, entrypoint.Options{
|
||||||
ConfigFname: *configFname,
|
ConfigFname: *configFname,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
|||||||
@@ -58,8 +58,6 @@ func buildJA4(hello *tls.ClientHelloInfo) (ja4 TLSFingerprintJA4) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch sslVersion {
|
switch sslVersion {
|
||||||
case tls.VersionSSL30:
|
|
||||||
buf = append(buf, 's', '3')
|
|
||||||
case tls.VersionTLS10:
|
case tls.VersionTLS10:
|
||||||
buf = append(buf, '1', '0')
|
buf = append(buf, '1', '0')
|
||||||
case tls.VersionTLS11:
|
case tls.VersionTLS11:
|
||||||
|
|||||||
Reference in New Issue
Block a user