Skip to content

Commit e16e979

Browse files
FiloSottilergdd
andcommitted
bastion,cmd/litebastion: add option to accept HTTP on localhost only
Closes #38 Co-authored-by: Rasmus Dahlberg <rasmus@rgdd.se>
1 parent 32fc8d4 commit e16e979

5 files changed

Lines changed: 282 additions & 26 deletions

File tree

bastion/bastion.go

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ import (
2020
"errors"
2121
"fmt"
2222
"log/slog"
23+
"net"
2324
"net/http"
2425
"net/http/httputil"
26+
"slices"
2527
"strings"
2628
"sync"
2729
"time"
@@ -51,6 +53,7 @@ type Bastion struct {
5153
c *Config
5254
proxy *httputil.ReverseProxy
5355
pool *backendConnectionsPool
56+
tls *tls.Config
5457
}
5558

5659
type keyHash [sha256.Size]byte
@@ -84,27 +87,14 @@ func New(c *Config) (*Bastion, error) {
8487
Transport: b.pool,
8588
ErrorLog: slog.NewLogLogger(b.pool.log.Handler(), slog.LevelDebug),
8689
}
87-
return b, nil
88-
}
89-
90-
// ConfigureServer sets up srv to handle backend connections to the bastion. It
91-
// wraps TLSConfig.GetConfigForClient to intercept backend connections, and sets
92-
// TLSNextProto for the bastion ALPN protocol. The original tls.Config is still
93-
// used for non-bastion backend connections.
94-
//
95-
// Note that since TLSNextProto won't be nil after a call to ConfigureServer,
96-
// the caller might want to call [http2.ConfigureServer] as well.
97-
func (b *Bastion) ConfigureServer(srv *http.Server) error {
98-
if srv.TLSNextProto == nil {
99-
srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
100-
}
101-
srv.TLSNextProto["bastion/0"] = b.pool.handleBackend
102-
103-
bastionTLSConfig := &tls.Config{
90+
b.tls = &tls.Config{
10491
MinVersion: tls.VersionTLS13,
10592
NextProtos: []string{"bastion/0"},
10693
ClientAuth: tls.RequireAnyClientCert,
10794
VerifyConnection: func(cs tls.ConnectionState) error {
95+
if cs.NegotiatedProtocol != "bastion/0" {
96+
return fmt.Errorf("missing ALPN")
97+
}
10898
h, err := backendHash(cs)
10999
if err != nil {
110100
return err
@@ -116,17 +106,47 @@ func (b *Bastion) ConfigureServer(srv *http.Server) error {
116106
},
117107
GetCertificate: b.c.GetCertificate,
118108
}
109+
return b, nil
110+
}
111+
112+
// HandleBackendConnection handles a new backend connection.
113+
//
114+
// It can be used alternatively to [Bastion.ConfigureServer] to accept backend
115+
// connections on a dedicated listener.
116+
func (b *Bastion) HandleBackendConnection(conn net.Conn) {
117+
tlsConn := tls.Server(conn, b.tls)
118+
if err := tlsConn.Handshake(); err != nil {
119+
b.pool.log.Debug("failed TLS handshake from backend", "err", err, "remote", conn.RemoteAddr())
120+
conn.Close()
121+
return
122+
}
123+
b.pool.Handle(tlsConn)
124+
conn.Close()
125+
}
126+
127+
// ConfigureServer sets up srv to handle backend connections to the bastion. It
128+
// wraps TLSConfig.GetConfigForClient to intercept backend connections, and sets
129+
// TLSNextProto for the bastion ALPN protocol. The original tls.Config is still
130+
// used for non-bastion backend connections.
131+
//
132+
// Note that since TLSNextProto won't be nil after a call to ConfigureServer,
133+
// the caller might want to call [http2.ConfigureServer] as well.
134+
func (b *Bastion) ConfigureServer(srv *http.Server) error {
135+
if srv.TLSNextProto == nil {
136+
srv.TLSNextProto = make(map[string]func(*http.Server, *tls.Conn, http.Handler))
137+
}
138+
srv.TLSNextProto["bastion/0"] = func(_ *http.Server, c *tls.Conn, _ http.Handler) {
139+
b.pool.Handle(c)
140+
}
119141

120142
if srv.TLSConfig == nil {
121143
srv.TLSConfig = &tls.Config{}
122144
}
123145
oldGetConfigForClient := srv.TLSConfig.GetConfigForClient
124146
srv.TLSConfig.GetConfigForClient = func(chi *tls.ClientHelloInfo) (*tls.Config, error) {
125-
for _, proto := range chi.SupportedProtos {
126-
if proto == "bastion/0" {
127-
// This is a bastion connection from a backend.
128-
return bastionTLSConfig, nil
129-
}
147+
if slices.Contains(chi.SupportedProtos, "bastion/0") {
148+
// This is a bastion connection from a backend.
149+
return b.tls, nil
130150
}
131151
if oldGetConfigForClient != nil {
132152
return oldGetConfigForClient(chi)
@@ -212,7 +232,7 @@ func (p *backendConnectionsPool) RoundTrip(r *http.Request) (*http.Response, err
212232
return cc.RoundTrip(r)
213233
}
214234

215-
func (p *backendConnectionsPool) handleBackend(hs *http.Server, c *tls.Conn, h http.Handler) {
235+
func (p *backendConnectionsPool) Handle(c *tls.Conn) {
216236
backend, err := backendHash(c.ConnectionState())
217237
if err != nil {
218238
p.log.Info("failed to get backend hash", "err", err)

cmd/litebastion/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ acceptable client/witness key hashes.
2323
-host string
2424
host to obtain ACME certificate for
2525

26+
If you intend to protect backends from unwanted traffic and not forward
27+
arbitrary requests from the internet, you can accept request on localhost.
28+
This is for example useful when running a bastion for your own log.
29+
30+
-listen-http PORT
31+
only accept HTTP requests at http://127.0.0.1:PORT
32+
2633
Since litebastion needs to operate at a lower level than HTTPS on the witness
2734
side, it can't be behind a reverse proxy, and needs to configure its own TLS
2835
certificate. Use the `-cache`, `-email`, and `-host` flags to configure the ACME

cmd/litebastion/litebastion.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"flag"
1919
"fmt"
2020
"log/slog"
21+
"net"
2122
"net/http"
2223
"os"
2324
"os/signal"
@@ -31,9 +32,11 @@ import (
3132
"golang.org/x/crypto/acme"
3233
"golang.org/x/crypto/acme/autocert"
3334
"golang.org/x/net/http2"
35+
"golang.org/x/sync/errgroup"
3436
)
3537

3638
var listenAddr = flag.String("listen", "localhost:8443", "host and port to listen at")
39+
var listenHTTPPort = flag.String("listen-http", "", "localhost port to listen for HTTP requests")
3740
var testCertificates = flag.Bool("testcert", false, "use localhost.pem and localhost-key.pem instead of ACME")
3841
var autocertCache = flag.String("cache", "", "directory to cache ACME certificates at")
3942
var autocertHost = flag.String("host", "", "host to obtain ACME certificate for")
@@ -145,6 +148,50 @@ func main() {
145148
})
146149
}
147150

151+
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
152+
defer stop()
153+
serveGroup, ctx := errgroup.WithContext(ctx)
154+
155+
if *listenHTTPPort != "" {
156+
hs := &http.Server{
157+
Addr: net.JoinHostPort("localhost", *listenHTTPPort),
158+
Handler: http.MaxBytesHandler(mux, 10*1024),
159+
ReadTimeout: 5 * time.Second,
160+
WriteTimeout: 5 * time.Second,
161+
}
162+
l, err := net.Listen("tcp", *listenAddr)
163+
if err != nil {
164+
logFatal("failed to listen for backends", "err", err)
165+
}
166+
serveGroup.Go(func() error {
167+
slog.Info("listening for HTTP", "addr", hs.Addr)
168+
return hs.ListenAndServe()
169+
})
170+
serveGroup.Go(func() error {
171+
slog.Info("listening for backends", "addr", *listenAddr)
172+
for {
173+
c, err := l.Accept()
174+
if err != nil {
175+
return err
176+
}
177+
go b.HandleBackendConnection(c)
178+
}
179+
})
180+
serveGroup.Go(func() error {
181+
<-ctx.Done()
182+
slog.Info("shutting down bastion listener")
183+
l.Close()
184+
slog.Info("shutting down HTTP server")
185+
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
186+
defer cancel()
187+
hs.Shutdown(ctx)
188+
return nil
189+
})
190+
serveGroup.Wait()
191+
slog.Info("exiting", "err", context.Cause(ctx))
192+
return
193+
}
194+
148195
hs := &http.Server{
149196
Addr: *listenAddr,
150197
Handler: http.MaxBytesHandler(mux, 10*1024),
@@ -163,8 +210,6 @@ func main() {
163210
}
164211

165212
slog.Info("listening", "addr", *listenAddr)
166-
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
167-
defer stop()
168213
e := make(chan error, 1)
169214
go func() { e <- hs.ListenAndServeTLS("", "") }()
170215
select {

cmd/litewitness/testdata/bastion.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,6 @@ KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
162162

163163
— sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562 UgIom7fPZTqpxWWhyjWduBvTvGVqsokMbqTArsQilegKoFBJQjUFAmQ0+YeSPM3wfUQMFSzVnnNuWRTYrajXpNUbIQY=
164164
```
165-
HTTP 200
165+
HTTP/2 200
166166
[Asserts]
167167
body contains "— example.com/witness"
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# gentest seed b4e385f4358f7373cfa9184b176f3cccf808e795baf04092ddfde9461014f0c4
2+
3+
# set up log
4+
exec witnessctl add-sigsum-log -key=ffdc2d4d98e4124d3feaf788c0c2f9abfd796083d1f0495437f302ec79cf100f
5+
6+
# start bastion
7+
exec litebastion -testcert -backends=backends.txt -listen localhost:8444 -listen-http 8080 &litebastion&
8+
waitfor localhost:8444
9+
10+
# start ssh-agent
11+
env SSH_AUTH_SOCK=$WORK/s # barely below the max path length
12+
! exec ssh-agent -a $SSH_AUTH_SOCK -D & # ssh-agent always exits 2
13+
waitfor $SSH_AUTH_SOCK
14+
chmod 600 witness_key.pem
15+
exec ssh-add witness_key.pem
16+
17+
# fail to start litewitness
18+
! exec litewitness -ssh-agent=$SSH_AUTH_SOCK -name=example.com/witness -bastion=0.0.0.0:443,localhost:8444 -testcert -key=e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a
19+
20+
# reload backends
21+
mv correct_backends.txt backends.txt
22+
exec killall -SIGHUP litebastion
23+
24+
# start litewitness
25+
exec litewitness -ssh-agent=$SSH_AUTH_SOCK -name=example.com/witness -bastion=0.0.0.0:443,localhost:8444 -testcert -key=e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a &litewitness&
26+
waitfor http://localhost:8080/e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a/
27+
28+
# add-checkpoint to bastion interface
29+
! exec hurl --cacert rootCA.pem --test add-checkpoint-public.hurl
30+
# add-checkpoint to local interface
31+
exec hurl --cacert rootCA.pem --test add-checkpoint.hurl
32+
33+
# check that litewitness shut down cleanly
34+
killall
35+
wait litewitness
36+
stderr 'shutting down'
37+
38+
# check the litebastion output
39+
killall
40+
wait litebastion
41+
stderr 'reloaded backends'
42+
stderr 'msg="accepted new backend connection" backend=e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a'
43+
44+
# witnessctl list-logs
45+
exec witnessctl list-logs
46+
stdout sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562
47+
stdout "size":1
48+
49+
50+
-- backends.txt --
51+
f97f12534ff2478cfc36b00d09a85d4faeb6589ac19a0895c348a499627c531c
52+
53+
54+
-- correct_backends.txt --
55+
f97f12534ff2478cfc36b00d09a85d4faeb6589ac19a0895c348a499627c531c
56+
e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a
57+
58+
59+
-- witness_key.pem --
60+
-----BEGIN OPENSSH PRIVATE KEY-----
61+
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
62+
c2gtZWQyNTUxOQAAACBkhIrYq+1uhZgbOzh1slK4dn67SwL3A6yjsecbvWqOUAAA
63+
AIgN5+09DeftPQAAAAtzc2gtZWQyNTUxOQAAACBkhIrYq+1uhZgbOzh1slK4dn67
64+
SwL3A6yjsecbvWqOUAAAAEAx/8IRbsvgA6yqgAq3B1e9fVMgbj/r72ptB5bZVTCz
65+
T2SEitir7W6FmBs7OHWyUrh2frtLAvcDrKOx5xu9ao5QAAAAAAECAwQF
66+
-----END OPENSSH PRIVATE KEY-----
67+
68+
69+
-- localhost.pem --
70+
-----BEGIN CERTIFICATE-----
71+
MIID/TCCAmWgAwIBAgIUPCwTk8pjBLPr+3czZAaGcg8RuDowDQYJKoZIhvcNAQEL
72+
BQAwNjELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRl
73+
c3QgUm9vdCBDQTAgFw0yNTA5MzAxMjAzMDdaGA8yMDU1MDkyMzEyMDMwN1owMDEL
74+
MAkGA1UEBhMCVVMxDTALBgNVBAoMBFRlc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDCC
75+
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKmcTiZ4FX92VKuQU9zioRv7
76+
nqsdEmMoOKs1nVJd7myLMi/02flEbDAmtDkh4LFPuiNw2OaqphBYM4MlZCxofKrL
77+
+/Skb763IOA5Ly97QGrOzYBtksPJlAPlF8pvRJO22ZIlBFpZUsivOZnFyhnvhFki
78+
TMMDpvNqtAAIee3Hv0y4EolhausAecaErlPc510LmcoU+VF5bYgur68nLzSvetzG
79+
qKblYkhFi3pEAJ7wcFUWZbFlNKDQVB+o4aEnqGj5mvYIbsh8UNJIqANA5qgxjoGc
80+
8F5eqWlXmFA9+R9lRe5JW6Ia91V8eKsTCRKuuFvH2gIs8pxrzV3fvdrXXHJCzxUC
81+
AwEAAaOBhjCBgzAUBgNVHREEDTALgglsb2NhbGhvc3QwCQYDVR0TBAIwADALBgNV
82+
HQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYDVR0OBBYEFO0cjjCGyrIC
83+
6ov0YT60AlyXsFRsMB8GA1UdIwQYMBaAFA3F8nWmTSb58CraZhp+Ur63NaC7MA0G
84+
CSqGSIb3DQEBCwUAA4IBgQA4EzY7MH95cFlVzbhk1loSlPRgDaWBga1y7U17jOnx
85+
ulqGr6ySInAounhZPDFfwIxyAbZE4N/Bq1omkfI+osvGBbkUdj7Z7ktllj5pON0t
86+
gdmCq/S1Q/tEcBvaS1FjR+AMu2enUVKn6mMXgjyj4xZNutqbXfKD4wdL1qP2KLbZ
87+
M832TqI2tVUVOGSq0zfsPe5sDD/NHSvkKaeM+ZGlB955fzBsICOx4W5mcz/Zhfk5
88+
gbJZsNPVlt8al3/iO0AIOGXzZOnlo8baHmh4TJH+uRYgHh8KFAvA8rebStg+Rn5w
89+
j+uAFgG+gf7JSVo6bjLWMkmkN6ooJFYMwwXav83STOU0TAQdqX+YM97Tke+U3DEd
90+
MqeX+hS+LyXGG0hHardo9xrRs1/W9xP453ybm2KFPODPyPSTuyOSUzleIeRGfmXg
91+
Ri++XfeZMNcPYNAj0joJEmoszmY17vuoBDidhYVQE+g+4uxhJeGROWvNz2ONqxqw
92+
QTyv4nS0hwl7T0VCoBC+bSg=
93+
-----END CERTIFICATE-----
94+
95+
96+
-- localhost-key.pem --
97+
-----BEGIN PRIVATE KEY-----
98+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCpnE4meBV/dlSr
99+
kFPc4qEb+56rHRJjKDirNZ1SXe5sizIv9Nn5RGwwJrQ5IeCxT7ojcNjmqqYQWDOD
100+
JWQsaHyqy/v0pG++tyDgOS8ve0Bqzs2AbZLDyZQD5RfKb0STttmSJQRaWVLIrzmZ
101+
xcoZ74RZIkzDA6bzarQACHntx79MuBKJYWrrAHnGhK5T3OddC5nKFPlReW2ILq+v
102+
Jy80r3rcxqim5WJIRYt6RACe8HBVFmWxZTSg0FQfqOGhJ6ho+Zr2CG7IfFDSSKgD
103+
QOaoMY6BnPBeXqlpV5hQPfkfZUXuSVuiGvdVfHirEwkSrrhbx9oCLPKca81d373a
104+
11xyQs8VAgMBAAECggEAH3wF38s7xmDrX7uXbbHeEUk4j3ACmUh+mH2H2iHYn+qI
105+
4vETQ1vJr3iHzPE2egOgPHL2uH7l+7O7wDUBLuMofTYHa8bYfXEWF7lVwn0hHJKO
106+
ADCW5WQ2ZzCwJWJZOwhew+u+Lp1VKi6oxRw7o2vcSAV/dVXouFfO2RC5vYNuRem5
107+
m/3xaOlDpPgmhkRV/I+AJk0f8zNSWpU+izonxqgSINREyemWVY705s+HirMEQgRV
108+
GN0ftwNGuz7/5rk8eH1iAGsTK9NEi77hCtyUKYhOC8NWzxGgS2mhqHslms8M2ZOm
109+
rBiZZ5Sh5U53r6IWCIKqG92fzFZ/fcSZGUDHrcjcYQKBgQDptvr/f21Ze9OCrGmk
110+
PktnM5v0DiP0z0Ywf28oz9rTneletJqQTsw6uP/GnIQrXwnaTWICjJtiMp9wxdM+
111+
9drKhZufImC0NGTLz1vJxLR5x5ao8T/juPIxm7TLfH5XjZMJgRMD1D5IvW8k9hWU
112+
1yDJxNCx1ikY7N3FP1XwmFrhRQKBgQC5yIeeABqXCy2A/wYrWqZ3zYFWe4t6Qqzm
113+
afuoUGaqXK9GjgDemOlUKzhUb08tnPNXzKJagqmM4F0zKgcwq61ECH3SliMt82PL
114+
1i1KTB2bUSXnwXOsbQyWVXz7YuVsWYakdHt6Vbbmy1JexRhfV3q5HCuj1fElNLTv
115+
YheLV0JLkQKBgEFuhR764fZngHPZKUpeVmXyQPs26kIjtZbmVoyqhK0yTJ/DGHLG
116+
XM8j9Bf6wdYSqYOAnqvwCaCYY6MC/31k/3grp8IJseFBueaFi0EV3SErC7cIs8Zh
117+
hQz2dstxcz232S6UAGrWBQoAXxmN+8TL5dYXUAY52w+rYPtUHA9b2DWxAoGAUlBL
118+
7jBjl5qnPalApYLTkO8nqBazFKdoDerVSpzc8AyCyELwla+wac+AdMCglzgcBUGw
119+
iWOtFbLu+FVdvC3EZglRHjXRPnHBPLYXePzCfWd14PowcywZ0J3t8z+9IMWFx2Wo
120+
s+o4UIezZjPzeYK76DpYB44p+u8gX5PZlK5DvFECgYBAKrvWEOy8J29QB3er+ERv
121+
qvmczAvfJWWgheKMxQhx/3pu6UW/v/hmcKngMWjr/XqxFKp05ca1VBCmPB3s0776
122+
IMJnvyTUFs5S7COpPJOC81JB/28UGQFcAZ+Kiwd1sx5gd1Q+EnaAhyxplWi/m6Zv
123+
tKfWSmtc1XhMiUpw331jNg==
124+
-----END PRIVATE KEY-----
125+
126+
127+
-- rootCA.pem --
128+
-----BEGIN CERTIFICATE-----
129+
MIIETzCCAregAwIBAgIUauyztCDIDrmE46pw/Pg5nn7U2gUwDQYJKoZIhvcNAQEL
130+
BQAwNjELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRl
131+
c3QgUm9vdCBDQTAgFw0yNTA5MzAxMjAyMzFaGA8yMDU1MDkyMzEyMDIzMVowNjEL
132+
MAkGA1UEBhMCVVMxEDAOBgNVBAoMB1Rlc3QgQ0ExFTATBgNVBAMMDFRlc3QgUm9v
133+
dCBDQTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAKHLd07/qunxEFL8
134+
BDCk1J+2m/TcUM0oG6dFVZjf/4+xSDH3F8tA6ZeqBLriQRBTuqwFBZVvoJwqJjip
135+
CAVzWkvvyiuvAeywJNXIe7kqmmDeAEoq2Z/F9j6p5y8+ddy7VX6pTdsVYOyGsBCx
136+
GZb7tuj0mtLl18HuezH0P7prDHBuNx+FjMblHXr5yslQ95d53v4dmI8ONObhS2ju
137+
ltgveHzaWbssaAqoVwfWJfWJtc0C3IgGh8MgFhhdzTQ4DvHxYLjwGB0eE/Vpu757
138+
B4pvYcT7JjBtjv8VyhW62ZPZNBlNjFOtFHhxZcMGDiOsyy9OgwIzzvy9UMg0WisZ
139+
OxZmRkASuApoMRftSg2ZmTG5THdFgEQD6pqE3llmhO57ZTt1DvPNUlUh8ND9u9Ov
140+
TcCjEeBXJRRjx7tUhob3mU+tYAi9DbXX/Wje0wBaRKh073wzRoAsFU9PspgKsZq4
141+
JNDFSey4DK8W/O8jYvmNfuQ3TJwiAWxNUDDUdGN3TwnaKRQesQIDAQABo1MwUTAd
142+
BgNVHQ4EFgQUDcXydaZNJvnwKtpmGn5Svrc1oLswHwYDVR0jBBgwFoAUDcXydaZN
143+
JvnwKtpmGn5Svrc1oLswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
144+
AYEAK4/oXvmhohTM10N+ZpaHDC8S+xr3z2tayJiNOrzzVE209MEOMCYlEz9IeyUx
145+
q5rmD4SJRLmyR8DpkO3dO4MjZYv2Nq+NS2kb5PmhUKmIEcwfFmXHjX/FCqPVwdH9
146+
yM63o3GS1PhbQ6vzK4NMsOUT3nvnD87gFvXKOSn+gpETmYNA55wD/Qcu8LhqDk0I
147+
YDrXiAja++gEf7/RZ5oPYNXi8t1+PjUDhP8J/tz9G90MZBR/FztTVIGTdpIoVwDB
148+
mMpAHW/7S9hEwVlZ69CUMfrm0uG68xaJPNH1MmaZdEdI0PnSU2nVwLLLqsYQmZqp
149+
+AGOq+6qOcvSMaFIJ3OLPUKqgn40f5J7I6retaER53Rahe/15Hy8H/sRsZQFPjeW
150+
qi86vkgd5GFSu2GNHN/Mhq7dHWq9U0mNO+BZt/G6du3gJ+SaO1UkJhzpNiRY37sR
151+
FU3HWF4uYVCt+OHqJ4we9XUKT80Y4ejNYkG25H+9DOy/Gtwb4qquqBoZYRUUThA1
152+
kJHt
153+
-----END CERTIFICATE-----
154+
155+
156+
-- add-checkpoint-public.hurl --
157+
POST https://localhost:8444/e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a/add-checkpoint
158+
```
159+
old 0
160+
161+
sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562
162+
1
163+
KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
164+
165+
— sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562 UgIom7fPZTqpxWWhyjWduBvTvGVqsokMbqTArsQilegKoFBJQjUFAmQ0+YeSPM3wfUQMFSzVnnNuWRTYrajXpNUbIQY=
166+
```
167+
HTTP 200
168+
[Asserts]
169+
body contains "— example.com/witness"
170+
171+
-- add-checkpoint.hurl --
172+
POST http://localhost:8080/e933707e0e36c30f01d94b5d81e742da373679d88eb0f85f959ccd80b83b992a/add-checkpoint
173+
```
174+
old 0
175+
176+
sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562
177+
1
178+
KgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
179+
180+
— sigsum.org/v1/tree/4d6d8825a6bb689d459628312889dfbb0bcd41b5211d9e1ce768b0ff0309e562 UgIom7fPZTqpxWWhyjWduBvTvGVqsokMbqTArsQilegKoFBJQjUFAmQ0+YeSPM3wfUQMFSzVnnNuWRTYrajXpNUbIQY=
181+
```
182+
HTTP 200
183+
[Asserts]
184+
body contains "— example.com/witness"

0 commit comments

Comments
 (0)