SEANK.H.LIAO

spire basics

spiife / spire getting started

spire basic go app

Here's a basic Go app, running in k8s with siffe / spire. It's basically a stripped down version of github.com/spiffe/go-spiffe/v2/examples/spiffe-http/client.

setup

Install spire server, and agent.

Register a workload for spire agent:

1$ kubectl exec -n spire spire-server-0 -- \
2    /opt/spire/bin/spire-server entry create \
3    -spiffeID spiffe://liao.dev/ns/spire/sa/spire-agent \
4    -selector k8s_psat:cluster:ihwa \
5    -selector k8s_psat:agent_ns:spire \
6    -selector k8s_psat:agent_sa:spire-agent \
7    -node

Register workloads for our test server / client

 1$ kubectl exec -n spire spire-server-0 -- \
 2    /opt/spire/bin/spire-server entry create \
 3    -spiffeID spiffe://ihwa.liao.dev/ns/default/sa/test-server \
 4    -parentID spiffe://ihwa.liao.dev/ns/spire/sa/spire-agent \
 5    -selector k8s:ns:default \
 6    -selector k8s:sa:test-server
 7$ kubectl exec -n spire spire-server-0 -- \
 8    /opt/spire/bin/spire-server entry create \
 9    -spiffeID spiffe://ihwa.liao.dev/ns/default/sa/test-client \
10    -parentID spiffe://ihwa.liao.dev/ns/spire/sa/spire-agent \
11    -selector k8s:ns:default \
12    -selector k8s:sa:test-client

server

go-spiffe has a bunch of packages, but the one we're interested in is workloadapi which can talk to the socket from the agent.

The cert from the client is in PeerCertificates as the only URI entry. VerifiedChains is not populated.

 1package main
 2
 3import (
 4        "context"
 5        "fmt"
 6        "io"
 7        "log"
 8        "net/http"
 9
10        "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
11        "github.com/spiffe/go-spiffe/v2/workloadapi"
12)
13
14func main() {
15        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
16                fmt.Println("peer", r.TLS.PeerCertificates[0].URIs[0].String()) // peer spiffe://ihwa.liao.dev/ns/default/sa/test-client
17                io.WriteString(w, "hello world")
18        })
19
20        ctx := context.Background()
21        // uses SPIFFE_ENDPOINT_SOCKET by default
22        source, err := workloadapi.NewX509Source(ctx)
23        if err != nil {
24                log.Fatalln("spiffe source:", err)
25        }
26        defer source.Close()
27        server := &http.Server{
28                Addr:      ":8443",
29                TLSConfig: tlsconfig.MTLSServerConfig(source, source, tlsconfig.AuthorizeAny()),
30        }
31
32        log.Println("starting")
33        log.Fatalln(server.ListenAndServeTLS("", ""))
34}

manifest:

 1apiVersion: v1
 2kind: Service
 3metadata:
 4  name: test-server
 5spec:
 6  ports:
 7    - name: https
 8      port: 443
 9      targetPort: https
10  selector:
11    app: test-server
12---
13apiVersion: v1
14kind: ServiceAccount
15metadata:
16  name: test-server
17---
18apiVersion: apps/v1
19kind: Deployment
20metadata:
21  name: test-server
22spec:
23  selector:
24    matchLabels:
25      app: test-server
26  template:
27    metadata:
28      labels:
29        app: test-server
30    spec:
31      serviceAccountName: test-server
32      containers:
33        - name: app
34          image: ko://go.seankhliao.com/testrepo0477/server
35          env:
36            - name: SPIFFE_ENDPOINT_SOCKET
37              value: unix:///run/spire/sockets/agent.sock
38          ports:
39            - name: https
40              containerPort: 8443
41          volumeMounts:
42            - name: spire-agent-socket
43              mountPath: /run/spire/sockets
44              readOnly: true
45      volumes:
46        - name: spire-agent-socket
47          hostPath:
48            path: /run/spire/sockets
49            type: Directory

client

Almost a copy of server

 1package main
 2
 3import (
 4        "context"
 5        "fmt"
 6        "io"
 7        "log"
 8        "net/http"
 9        "os"
10        "time"
11
12        "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
13        "github.com/spiffe/go-spiffe/v2/workloadapi"
14)
15
16func main() {
17        ctx := context.Background()
18        source, err := workloadapi.NewX509Source(ctx)
19        if err != nil {
20                log.Fatalln("spiffe source:", err)
21        }
22        defer source.Close()
23
24        tlsConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeAny())
25        client := &http.Client{
26                Transport: &http.Transport{
27                        TLSClientConfig: tlsConfig,
28                },
29        }
30
31        log.Println("starting client loop")
32        for range time.NewTicker(5 * time.Second).C {
33                func() {
34                        r, err := client.Get("https://test-server")
35                        if err != nil {
36                                log.Fatalln("get from server", err)
37                        }
38                        defer r.Body.Close()
39                        fmt.Println("\npeer", r.TLS.PeerCertificates[0].URIs[0].String()) // peer spiffe://ihwa.liao.dev/ns/default/sa/test-server
40                        io.Copy(os.Stdout, r.Body)
41                }()
42        }
43}

manifest:

 1apiVersion: v1
 2kind: ServiceAccount
 3metadata:
 4  name: test-client
 5---
 6apiVersion: apps/v1
 7kind: Deployment
 8metadata:
 9  name: test-client
10spec:
11  selector:
12    matchLabels:
13      app: test-client
14  template:
15    metadata:
16      labels:
17        app: test-client
18    spec:
19      serviceAccountName: test-client
20      containers:
21        - name: app
22          image: ko://go.seankhliao.com/testrepo0477/client
23          env:
24            - name: SPIFFE_ENDPOINT_SOCKET
25              value: unix:///run/spire/sockets/agent.sock
26          volumeMounts:
27            - name: spire-agent-socket
28              mountPath: /run/spire/sockets
29              readOnly: true
30      volumes:
31        - name: spire-agent-socket
32          hostPath:
33            path: /run/spire/sockets
34            type: Directory