Skip to content

Persistent TLS certificates #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions cmd/receiver-proxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"log"
"net/http"
"net/http/pprof"
Expand All @@ -23,6 +24,9 @@ const (
flagSystemListenAddr = "system-listen-addr"
flagCertListenAddr = "cert-listen-addr"
flagMaxUserRPS = "max-user-requests-per-second"

flagCertPath = "cert-path"
flagCertKeyPath = "cert-key-path"
)

var flags = []cli.Flag{
Expand Down Expand Up @@ -111,6 +115,16 @@ var flags = []cli.Flag{
Usage: "generated certificate hosts",
EnvVars: []string{"CERT_HOSTS"},
},
&cli.DurationFlag{
Name: flagCertPath,
Usage: "path where to store the generated certificate",
EnvVars: []string{"CERT_PATH"},
},
&cli.StringFlag{
Name: flagCertKeyPath,
Usage: "path where to store the generated certificate key",
EnvVars: []string{"CERT_KEY_PATH"},
},

// Logging, metrics and debug
&cli.StringFlag{
Expand Down Expand Up @@ -216,8 +230,7 @@ func runMain(cCtx *cli.Context) error {

builderEndpoint := cCtx.String("builder-endpoint")
rpcEndpoint := cCtx.String("rpc-endpoint")
certDuration := cCtx.Duration("cert-duration")
certHosts := cCtx.StringSlice("cert-hosts")

builderConfigHubEndpoint := cCtx.String("builder-confighub-endpoint")
archiveEndpoint := cCtx.String("orderflow-archive-endpoint")
flashbotsSignerStr := cCtx.String("flashbots-orderflow-signer-address")
Expand All @@ -227,10 +240,20 @@ func runMain(cCtx *cli.Context) error {

maxUserRPS := cCtx.Int(flagMaxUserRPS)

certDuration := cCtx.Duration("cert-duration")
certHosts := cCtx.StringSlice("cert-hosts")
certPath := cCtx.String(flagCertPath)
certKeyPath := cCtx.String(flagCertKeyPath)
if certPath == "" || certKeyPath == "" {
return errors.New("cert-path and cert-key-path must be set")
}

proxyConfig := &proxy.ReceiverProxyConfig{
ReceiverProxyConstantConfig: proxy.ReceiverProxyConstantConfig{Log: log, FlashbotsSignerAddress: flashbotsSignerAddress},
CertValidDuration: certDuration,
CertHosts: certHosts,
CertPath: certPath,
CertKeyPath: certKeyPath,
BuilderConfigHubEndpoint: builderConfigHubEndpoint,
ArchiveEndpoint: archiveEndpoint,
ArchiveConnections: connectionsPerPeer,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/VictoriaMetrics/metrics v1.35.1
github.com/cenkalti/backoff v2.2.1+incompatible
github.com/ethereum/go-ethereum v1.15.5
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5
github.com/flashbots/go-utils v0.11.0
github.com/google/uuid v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/stretchr/testify v1.10.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ github.com/ethereum/go-ethereum v1.15.5 h1:Fo2TbBWC61lWVkFw9tsMoHCNX1ndpuaQBRJ8H
github.com/ethereum/go-ethereum v1.15.5/go.mod h1:1LG2LnMOx2yPRHR/S+xuipXH29vPr6BIH6GElD8N/fo=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/flashbots/go-utils v0.10.0 h1:75XWewRO5GIhdLn8+vqdzzuoqJh+j8wN54A++Id7W0Y=
github.com/flashbots/go-utils v0.10.0/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5 h1:oEfjmk2NQ/FtX+1h1Rx4PMpJtbNAa6T/l6otbUmzZp8=
github.com/flashbots/go-utils v0.10.1-0.20250416132112-39233b64a8c5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5 h1:F6pO8NC+JSG8jSFzTOAjP4TaR+CwZKygQ4vSLT+XPPw=
github.com/flashbots/go-utils v0.10.1-0.20250424105414-d6d0a6a11ce5/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/flashbots/go-utils v0.11.0 h1:MuI9OOl40MukSL2ucKKQG1sxxl5Cqjla41TRubGNu0w=
github.com/flashbots/go-utils v0.11.0/go.mod h1:i4xxEB6sHDFfNWEIfh+rP6nx3LxynEn8AOZa05EYgwA=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
Expand Down
39 changes: 23 additions & 16 deletions proxy/html/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,40 +120,47 @@ <h2>Sending Orderflow to BuilderNet</h2>
<h3>Example <span class="code-tag">curl</span> Request</h3>
<pre>curl https://_BUILDERNET_INSTANCE_ \
--cacert builder-cert.pem \ # or using --insecure
--header 'Content-Type: application/json' \
--header 'X-Flashbots-Signature: _public_key_address_:_signature_' \
--data '{
"jsonrpc":"2.0",
"method":"eth_sendRawTransaction",
"params":["0x000000..."],
"id":1
}'</pre>
<p>See also the full documentation at <a href="https://buildernet.org/docs/send-orderflow">buildernet.org/docs/send-orderflow</a></p>

<div class="note">
<strong>Note:</strong> Currently, requests are rate-limited to 3 requests / IP / second. This is expected to be raised soon.
</div>
"id": 1,
"jsonrpc": "2.0",
"method": "eth_sendBundle",
"params": [{
"txs": [
"0x100000...", "0x200000..."
],
"blockNumber": "0x1361bd3"
}]
}'
</pre>
<p>See also the full documentation at
<ul>
<li><a href="https://buildernet.org/docs/api">buildernet.org/docs/api</a></li>
<li><a href="https://buildernet.org/docs/send-orderflow">buildernet.org/docs/send-orderflow</a></li>
</ul>
</p>

<h3>Downloading the TLS certificate</h3>

<p>You can get the TLS certificate through a TEE-attested channel (see "TEE Proof Validation" below), or download it directly with <tt>curl</tt>:</p>
<pre>curl -w %{certs} -k https://_BUILDERNET_INSTANCE_</pre>

<div class="note">
<strong>Note:</strong> Currently, requests are rate-limited to 3 requests / IP / second. This is expected to be raised soon.
</div>

</section>

<hr>

<section class="section">
<h2>Instance TLS Certificate</h2>

<p>BuilderNet instances create (and rotate) their own unique TLS certificate, and use it to prove their identity and to encrypt incoming traffic.</p>
<p>BuilderNet instances create their own unique TLS certificate, and use it to prove their identity and to encrypt incoming traffic.</p>
<p>This is the TLS certificate of this specific instance:</p>

<pre>{{ .Cert }}</pre>

<div class="note">
<strong>Note:</strong> The certificate changes on every restart of the server. This is a feature, not a bug, as the certificate
represents the unique identity of this server, and the private key never leaves the in-process memory.
</div>
</section>

<hr>
Expand Down
5 changes: 4 additions & 1 deletion proxy/receiver_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ type ReceiverProxyConstantConfig struct {

type ReceiverProxyConfig struct {
ReceiverProxyConstantConfig

CertValidDuration time.Duration
CertHosts []string
CertPath string
CertKeyPath string

BuilderConfigHubEndpoint string
ArchiveEndpoint string
Expand All @@ -99,7 +102,7 @@ func NewReceiverProxy(config ReceiverProxyConfig) (*ReceiverProxy, error) {
return nil, err
}

cert, key, err := utils_tls.GenerateTLS(config.CertValidDuration, config.CertHosts)
cert, key, err := utils_tls.GetOrGenerateTLS(config.CertPath, config.CertKeyPath, config.CertValidDuration, config.CertHosts)
if err != nil {
return nil, err
}
Expand Down
43 changes: 33 additions & 10 deletions proxy/receiver_proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"net/http"
"net/http/httptest"
"os"
"path"
"testing"
"time"

Expand Down Expand Up @@ -74,11 +75,11 @@ func (setup *OrderflowProxyTestSetup) Close() {
setup.proxy.Stop()
}

func StartTestOrderflowProxy(name string) (*OrderflowProxyTestSetup, error) {
func StartTestOrderflowProxy(name, certPath, certKeyPath string) (*OrderflowProxyTestSetup, error) {
localBuilderRequests := make(chan *RequestData, 1)
localBuilderServer := ServeHTTPRequestToChan(localBuilderRequests)

proxy := createProxy(localBuilderServer.URL, name)
proxy := createProxy(localBuilderServer.URL, name, certPath, certKeyPath)
publicProxyServer := &http.Server{ //nolint:gosec
Handler: proxy.SystemHandler,
TLSConfig: proxy.TLSConfig(),
Expand Down Expand Up @@ -117,6 +118,12 @@ func StartTestOrderflowProxy(name string) (*OrderflowProxyTestSetup, error) {
}, nil
}

func check(err error) {
if err != nil {
panic(err)
}
}

func TestMain(m *testing.M) {
signer, err := signature.NewRandomSigner()
if err != nil {
Expand Down Expand Up @@ -168,32 +175,45 @@ func TestMain(m *testing.M) {
archiveServer = ServeHTTPRequestToChan(archiveServerRequests)
defer archiveServer.Close()

tempDirs := make([]string, 0)
for i := range 3 {
proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i))
tempDir, err := os.MkdirTemp("", "orderflow-proxy-test")
check(err)
tempDirs = append(tempDirs, tempDir)
certPath := path.Join(tempDir, "cert")
keyPath := path.Join(tempDir, "key")

proxy, err := StartTestOrderflowProxy(fmt.Sprintf("proxy:%d", i), certPath, keyPath)
proxies = append(proxies, proxy)
if err != nil {
panic(err)
}
check(err)
}

defer func() {
for _, prx := range proxies {
prx.Close()
}

for _, dir := range tempDirs {
_ = os.RemoveAll(dir)
}
}()

os.Exit(m.Run())
}

func createProxy(localBuilder, name string) *ReceiverProxy {
func createProxy(localBuilder, name, certPath, certKeyPath string) *ReceiverProxy {
log := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
proxy, err := NewReceiverProxy(ReceiverProxyConfig{
ReceiverProxyConstantConfig: ReceiverProxyConstantConfig{
Log: log,
Name: name,
FlashbotsSignerAddress: flashbotsSigner.Address(),
},
CertValidDuration: time.Hour * 24,
CertHosts: []string{"localhost", "127.0.0.1"},
CertValidDuration: time.Hour * 24,
CertHosts: []string{"localhost", "127.0.0.1"},
CertPath: certPath,
CertKeyPath: certKeyPath,

BuilderConfigHubEndpoint: builderHub.URL,
ArchiveEndpoint: archiveServer.URL,
LocalBuilderEndpoint: localBuilder,
Expand Down Expand Up @@ -551,7 +571,10 @@ func TestProxyBidSubsidiseBlockCall(t *testing.T) {
}

func TestBuilderNetRootCall(t *testing.T) {
proxy, err := StartTestOrderflowProxy("1")
tempDir := t.TempDir()
certPath := path.Join(tempDir, "cert")
keyPath := path.Join(tempDir, "key")
proxy, err := StartTestOrderflowProxy("1", certPath, keyPath)
require.NoError(t, err)

req := httptest.NewRequest(http.MethodGet, "/", nil)
Expand Down