caddy_clienthello

package module
v0.0.0-...-5cbe00f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2025 License: Apache-2.0 Imports: 13 Imported by: 0

README

caddy-clienthello

A caddy plugin to forward TLS ClientHello packets on requests as a header.

Building locally

CGO_ENABLED=1 xcaddy build \
    --with github.com/mholt/caddy-ratelimit \
    --with github.com/prosopo/chaddy=/path/to/chaddy/repo \

to build caddy, which will output a bin file in your cwd. Then

./caddy run --config ./path/to/the/Caddyfile

Building with xcaddy

xcaddy build \
  --with github.com/prosopo/chaddy

Sample Caddyfile

Note that this enforces HTTPS (TLS).
You can add a http_redirect to automatically redirect http -> https like shown below.

TLS ClientHellos do not exist on HTTP/3 connections. No X-TLS-ClientHello header will be present on such requests. I recommended to disable HTTP/3.

{
    order ja3 before reverse_proxy
    client_hello {
        # Configure the maximum allowed ClientHello packet size in bytes (1-16384)
        max_client_hello_size 16384
    }
    servers {
        # Disable HTTP/3
        protocols h1 h2

        listener_wrappers {
            http_redirect
            client_hello
            tls
        }
    }
}

localhost {
    client_hello

    # ClientHello will be available as the `X-TLS-ClientHello` header 
    reverse_proxy http://other.service
}

Details

The X-TLS-ClientHello header will be present on all requests that use an underlying TLS connection. It contains the raw ClientHello bytes as a base64 encoded string.

If the ClientHello exceeds the configured max_client_hello_size in bytes, then the X-TLS-ClientHello header will instead be set to the value EXCEEDS_MAXIMUM_SIZE. The maximum allowed size value should be carefully selected as I have observed sizes ranging anywhere from 200 to 2500 bytes and possibly more.

In the case of an internal error and a missing X-TLS-ClientHello header, this is not representative of a suspicious client and should not be factored in to a bot score.

This module also disables TLS session resumption globally to always retrieve a full ClientHello. This is done through the usage of caddytls's session_tickets/disabled config option automatically.

Documentation

Index

Constants

View Source
const (
	CacheAppId = "client_hello.cache"
)
View Source
const (
	ConfigAppId = "client_hello.config"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Cache

type Cache struct {
	// contains filtered or unexported fields
}

func (Cache) CaddyModule

func (Cache) CaddyModule() caddy.ModuleInfo

CaddyModule implements caddy.Module

func (*Cache) ClearClientHello

func (c *Cache) ClearClientHello(addr string)

func (*Cache) GetClientHello

func (c *Cache) GetClientHello(addr string) *string

func (*Cache) Provision

func (c *Cache) Provision(ctx caddy.Context) error

func (*Cache) SetClientHello

func (c *Cache) SetClientHello(addr string, encoded string)

func (*Cache) Start

func (c *Cache) Start() error

Start implements caddy.App

func (*Cache) Stop

func (c *Cache) Stop() error

Stop implements caddy.App

type CacheEntry

type CacheEntry struct {
	Value string
}

type ClientHelloConnWrapper

type ClientHelloConnWrapper struct {
	net.Conn // embed the net.Conn interface (crucial for setReadTimeout to be applied by caddy to avoid blocking reads!)
	// contains filtered or unexported fields
}

ClientHelloConnWrapper is a custom wrapper for net.Conn that intercepts the Read operation and allows us to collect the client hello bytes

func NewClientHelloConnWrapper

func NewClientHelloConnWrapper(conn net.Conn, cache *Cache, log *zap.Logger) *ClientHelloConnWrapper

NewClientHelloConnWrapper creates a new wrapper

func (*ClientHelloConnWrapper) Close

func (r *ClientHelloConnWrapper) Close() error

Close closes the connection

func (*ClientHelloConnWrapper) Read

func (r *ClientHelloConnWrapper) Read(b []byte) (n int, err error)

Read intercepts the bytes being read from the connection. We can catch the client hello bytes here by peeking the client hello bytes from the connection.

type ClientHelloHandler

type ClientHelloHandler struct {
	// contains filtered or unexported fields
}

func (ClientHelloHandler) CaddyModule

func (ClientHelloHandler) CaddyModule() caddy.ModuleInfo

CaddyModule implements caddy.Module

func (*ClientHelloHandler) Provision

func (h *ClientHelloHandler) Provision(ctx caddy.Context) error

Provision implements caddy.Provisioner

func (*ClientHelloHandler) ServeHTTP

ServeHTTP implements caddyhttp.MiddlewareHandler

func (*ClientHelloHandler) UnmarshalCaddyfile

func (h *ClientHelloHandler) UnmarshalCaddyfile(_ *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler

type ClientHelloListenerWrapper

type ClientHelloListenerWrapper struct {
	// contains filtered or unexported fields
}

func (ClientHelloListenerWrapper) CaddyModule

CaddyModule implements caddy.Module

func (*ClientHelloListenerWrapper) Provision

func (l *ClientHelloListenerWrapper) Provision(ctx caddy.Context) error

func (*ClientHelloListenerWrapper) UnmarshalCaddyfile

func (l *ClientHelloListenerWrapper) UnmarshalCaddyfile(_ *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler

func (*ClientHelloListenerWrapper) WrapListener

func (l *ClientHelloListenerWrapper) WrapListener(ln net.Listener) net.Listener

WrapListener implements caddy.ListenerWrapper

type Config

type Config struct {
	MaxClientHelloSize uint16 `json:"max_client_hello_size"`
}

func (Config) CaddyModule

func (Config) CaddyModule() caddy.ModuleInfo

CaddyModule implements caddy.Module

func (*Config) Provision

func (c *Config) Provision(ctx caddy.Context) error

Provision implements caddy.Provisioner

func (*Config) Start

func (c *Config) Start() error

Start implements caddy.App

func (*Config) Stop

func (c *Config) Stop() error

Stop implements caddy.App

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL