Skip to content

Commit 474f26b

Browse files
committed
Initial commit
1 parent d3ff623 commit 474f26b

File tree

4 files changed

+212
-1
lines changed

4 files changed

+212
-1
lines changed

README.md

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,33 @@
11
# spiffe-pinger
2-
Small binary for testing the SPIFFE Workload API and using SPIFFE for mTLS with gRPC
2+
3+
Small utility for testing SPIFFE functionality.
4+
5+
The service:
6+
7+
- Connects to a SPIFFE Workload API to retrieve an X509 SVID
8+
- Spins up a gRPC server that listens on a TCP address, and is protected by TLS
9+
using the X509 SVID
10+
- Spins up a loop that pings a gRPC server using the X509 SVID as a client
11+
certificate
12+
13+
Spin up two of these and point them at one another e.g
14+
15+
```shell
16+
SPIFFE_ENDPOINT_SOCKET=unix:///tmp/workload-socket-a.sock LISTEN=127.0.0.1:1338 TARGET=127.0.0.1:1337 go run ./main.go
17+
```
18+
19+
```shell
20+
SPIFFE_ENDPOINT_SOCKET=unix:///tmp/workload-socket-b.sock LISTEN=127.0.0.1:1337 TARGET=127.0.0.1:1338 go run ./main.go
21+
```
22+
23+
The logs will indicate the identity of the service itself, and the identity of
24+
any client which connects to it:
25+
26+
```shell
27+
2024/08/30 13:12:36 INFO Sent message me=spiffe://leaf.tele.ottr.sh/example component=client
28+
2024/08/30 13:12:37 INFO Received request me=spiffe://leaf.tele.ottr.sh/example component=server from=spiffe://spire.tele.ottr.sh/macbook/noah
29+
2024/08/30 13:12:41 INFO Sent message me=spiffe://leaf.tele.ottr.sh/example component=client
30+
2024/08/30 13:12:42 INFO Received request me=spiffe://leaf.tele.ottr.sh/example component=server from=spiffe://spire.tele.ottr.sh/macbook/noah
31+
2024/08/30 13:12:46 INFO Sent message me=spiffe://leaf.tele.ottr.sh/example component=client
32+
2024/08/30 13:12:47 INFO Received request me=spiffe://leaf.tele.ottr.sh/example component=server from=spiffe://spire.tele.ottr.sh/macbook/noah
33+
```

go.mod

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module github.com/strideynet/spiffe-pinger
2+
3+
go 1.23.0
4+
5+
require (
6+
github.com/spiffe/go-spiffe/v2 v2.3.0
7+
golang.org/x/sync v0.8.0
8+
google.golang.org/grpc v1.66.0
9+
google.golang.org/grpc/examples v0.0.0-20240829213413-845f62caf49a
10+
)
11+
12+
require (
13+
github.com/Microsoft/go-winio v0.6.2 // indirect
14+
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
15+
github.com/zeebo/errs v1.3.0 // indirect
16+
golang.org/x/crypto v0.26.0 // indirect
17+
golang.org/x/net v0.28.0 // indirect
18+
golang.org/x/sys v0.24.0 // indirect
19+
golang.org/x/text v0.17.0 // indirect
20+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
21+
google.golang.org/protobuf v1.34.2 // indirect
22+
)

go.sum

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
2+
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
3+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
4+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5+
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
6+
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
7+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
8+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
9+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
10+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
11+
github.com/spiffe/go-spiffe/v2 v2.3.0 h1:g2jYNb/PDMB8I7mBGL2Zuq/Ur6hUhoroxGQFyD6tTj8=
12+
github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7zvJnTV8ZyIY=
13+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
14+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
15+
github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs=
16+
github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4=
17+
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
18+
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
19+
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
20+
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
21+
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
22+
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
23+
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
24+
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
25+
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
26+
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
27+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 h1:e7S5W7MGGLaSu8j3YjdezkZ+m1/Nm0uRVRMEMGk26Xs=
28+
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
29+
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
30+
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
31+
google.golang.org/grpc/examples v0.0.0-20240829213413-845f62caf49a h1:QKnUKSrAlVuW9jhTk7vcVa8bA82nW36VJjxgFdWfuq4=
32+
google.golang.org/grpc/examples v0.0.0-20240829213413-845f62caf49a/go.mod h1:eU5SdK0I2UOcKBHSSunTStQLQ1EJGCHKskH7zg51D5Y=
33+
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
34+
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
35+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
36+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

main.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"log/slog"
7+
"net"
8+
"os"
9+
"time"
10+
11+
"github.com/spiffe/go-spiffe/v2/spiffegrpc/grpccredentials"
12+
"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
13+
"github.com/spiffe/go-spiffe/v2/workloadapi"
14+
"golang.org/x/sync/errgroup"
15+
"google.golang.org/grpc"
16+
pb "google.golang.org/grpc/examples/helloworld/helloworld"
17+
)
18+
19+
// server is used to implement helloworld.GreeterServer.
20+
type server struct {
21+
pb.UnimplementedGreeterServer
22+
log *slog.Logger
23+
}
24+
25+
// SayHello implements helloworld.GreeterServer
26+
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
27+
peer, ok := grpccredentials.PeerIDFromContext(ctx)
28+
if !ok {
29+
s.log.Info("Failed to get peer ID")
30+
} else {
31+
s.log.Info("Received request", "from", peer.String())
32+
}
33+
return &pb.HelloReply{Message: "Pong"}, nil
34+
}
35+
36+
func main() {
37+
if err := run(context.Background()); err != nil {
38+
slog.Error("encountered fatal error", "error", err)
39+
os.Exit(1)
40+
}
41+
}
42+
43+
func run(ctx context.Context) error {
44+
socketPath := os.Getenv("SPIFFE_ENDPOINT_SOCKET")
45+
if socketPath == "" {
46+
return fmt.Errorf("SPIFFE_ENDPOINT_SOCKET is not defined")
47+
}
48+
listen := os.Getenv("LISTEN")
49+
if listen == "" {
50+
return fmt.Errorf("LISTEN is not defined")
51+
}
52+
target := os.Getenv("TARGET")
53+
if target == "" {
54+
return fmt.Errorf("TARGET is not defined")
55+
}
56+
57+
source, err := workloadapi.NewX509Source(ctx, workloadapi.WithClientOptions(workloadapi.WithAddr(socketPath)))
58+
if err != nil {
59+
return fmt.Errorf("unable to create X509Source: %w", err)
60+
}
61+
defer source.Close()
62+
63+
svid, err := source.GetX509SVID()
64+
if err != nil {
65+
return fmt.Errorf("unable to get X509SVID: %w", err)
66+
}
67+
68+
log := slog.With("me", svid.ID.String())
69+
70+
eg, egCtx := errgroup.WithContext(ctx)
71+
eg.Go(func() error {
72+
// Create a server with credentials that do mTLS and verify that the presented certificate has SPIFFE ID `spiffe://example.org/client`
73+
s := grpc.NewServer(grpc.Creds(
74+
grpccredentials.MTLSServerCredentials(source, source, tlsconfig.AuthorizeAny()),
75+
))
76+
77+
lis, err := net.Listen("tcp", listen)
78+
if err != nil {
79+
return fmt.Errorf("error creating listener: %w", err)
80+
}
81+
82+
pb.RegisterGreeterServer(s, &server{
83+
log: log.With("component", "server"),
84+
})
85+
stop := context.AfterFunc(egCtx, s.Stop)
86+
defer stop()
87+
if err := s.Serve(lis); err != nil {
88+
return fmt.Errorf("failed to serve: %w", err)
89+
}
90+
return nil
91+
})
92+
eg.Go(func() error {
93+
log := log.With("component", "client")
94+
s, err := grpc.NewClient(target, grpc.WithTransportCredentials(
95+
grpccredentials.MTLSClientCredentials(source, source, tlsconfig.AuthorizeAny()),
96+
))
97+
if err != nil {
98+
return fmt.Errorf("failed to create client: %w", err)
99+
}
100+
defer s.Close()
101+
102+
greeterClient := pb.NewGreeterClient(s)
103+
104+
for {
105+
_, err := greeterClient.SayHello(egCtx, &pb.HelloRequest{Name: "Ping"})
106+
if err != nil {
107+
log.Error("Received error", "error", err)
108+
} else {
109+
log.Info("Sent message")
110+
}
111+
112+
select {
113+
case <-egCtx.Done():
114+
return nil
115+
case <-time.After(5 * time.Second):
116+
117+
}
118+
}
119+
})
120+
121+
return eg.Wait()
122+
}

0 commit comments

Comments
 (0)