Skip to content

Commit 1deff2a

Browse files
committed
Add SPIRE-backed Prometheus TLS identity and SPIFFE allowlist
(cherry picked from commit 2ec839e) Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
1 parent db8dcd9 commit 1deff2a

11 files changed

Lines changed: 531 additions & 47 deletions

File tree

conf/agent/agent_full.conf

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,9 +526,29 @@ plugins {
526526

527527
# # optional TLS configuration for Prometheus exporter.
528528
# tls {
529+
# # use_spire_svid: Use the current SPIRE SVID for the Prometheus
530+
# # endpoint instead of cert_file/key_file.
531+
# # use_spire_svid = false
532+
#
533+
# # authorized_spiffe_ids: Optional list of SPIFFE IDs allowed to
534+
# # connect to the Prometheus endpoint. Cannot be used with
535+
# # client_ca_file.
536+
# # authorized_spiffe_ids = ["spiffe://example.org/monitoring/prometheus"]
537+
#
538+
# # cert_file: Path to the PEM-encoded certificate to serve from
539+
# # the Prometheus endpoint. Must be set with key_file unless
540+
# # use_spire_svid is enabled.
529541
# cert_file = "/path/to/cert.pem"
542+
#
543+
# # key_file: Path to the PEM-encoded private key to serve from the
544+
# # Prometheus endpoint. Must be set with cert_file unless
545+
# # use_spire_svid is enabled.
530546
# key_file = "/path/to/key.pem"
531-
# client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
547+
#
548+
# # client_ca_file: Optional PEM-encoded CA bundle used to verify
549+
# # Prometheus clients for mTLS. Cannot be used with
550+
# # authorized_spiffe_ids.
551+
# client_ca_file = "/path/to/ca.pem"
532552
# }
533553
# }
534554

conf/server/server_full.conf

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1001,9 +1001,29 @@ plugins {
10011001

10021002
# # optional TLS configuration for Prometheus exporter.
10031003
# tls {
1004+
# # use_spire_svid: Use the current SPIRE SVID for the Prometheus
1005+
# # endpoint instead of cert_file/key_file.
1006+
# # use_spire_svid = false
1007+
#
1008+
# # authorized_spiffe_ids: Optional list of SPIFFE IDs allowed to
1009+
# # connect to the Prometheus endpoint. Cannot be used with
1010+
# # client_ca_file.
1011+
# # authorized_spiffe_ids = ["spiffe://example.org/monitoring/prometheus"]
1012+
#
1013+
# # cert_file: Path to the PEM-encoded certificate to serve from
1014+
# # the Prometheus endpoint. Must be set with key_file unless
1015+
# # use_spire_svid is enabled.
10041016
# cert_file = "/path/to/cert.pem"
1017+
#
1018+
# # key_file: Path to the PEM-encoded private key to serve from the
1019+
# # Prometheus endpoint. Must be set with cert_file unless
1020+
# # use_spire_svid is enabled.
10051021
# key_file = "/path/to/key.pem"
1006-
# client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
1022+
#
1023+
# # client_ca_file: Optional PEM-encoded CA bundle used to verify
1024+
# # Prometheus clients for mTLS. Cannot be used with
1025+
# # authorized_spiffe_ids.
1026+
# client_ca_file = "/path/to/ca.pem"
10071027
# }
10081028
# }
10091029

doc/spire_agent.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,17 @@ agent {
381381
telemetry {
382382
Prometheus {
383383
port = 1234
384-
#optional TLS for prometheus
384+
# optional TLS for prometheus
385385
tls {
386-
cert_file = "/path/to/cert.pem"
387-
key_file = "/path/to/key.pem"
388-
client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
386+
use_spire_svid = true
387+
authorized_spiffe_ids = [
388+
"spiffe://example.org/monitoring/prometheus",
389+
]
390+
391+
# Alternatively, configure a web certificate directly:
392+
# cert_file = "/path/to/cert.pem"
393+
# key_file = "/path/to/key.pem"
394+
# client_ca_file = "/path/to/ca.pem"
389395
}
390396
}
391397
}

doc/spire_server.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -821,11 +821,17 @@ server {
821821
telemetry {
822822
Prometheus {
823823
port = 1234
824-
#optional TLS for prometheus
824+
# optional TLS for prometheus
825825
tls {
826-
cert_file = "/path/to/cert.pem"
827-
key_file = "/path/to/key.pem"
828-
client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
826+
use_spire_svid = true
827+
authorized_spiffe_ids = [
828+
"spiffe://example.org/monitoring/prometheus",
829+
]
830+
831+
# Alternatively, configure a web certificate directly:
832+
# cert_file = "/path/to/cert.pem"
833+
# key_file = "/path/to/key.pem"
834+
# client_ca_file = "/path/to/ca.pem"
829835
}
830836
}
831837
}

doc/telemetry/telemetry_config.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ You may use all, some, or none of the collectors. The following collectors suppo
3838
|---------------|----------|------------------------------------|
3939
| `host` | `string` | Prometheus exporter listen address |
4040
| `port` | `int` | Prometheus exporter listen port |
41+
| `tls` | `object` | TLS configuration for the Prometheus exporter |
42+
43+
#### `Prometheus.tls`
44+
45+
| Configuration | Type | Description |
46+
|---------------|------|-------------|
47+
| `cert_file` | `string` | Path to the PEM-encoded certificate for the Prometheus exporter. Must be set with `key_file` unless `use_spire_svid` is enabled |
48+
| `key_file` | `string` | Path to the PEM-encoded private key for the Prometheus exporter. Must be set with `cert_file` unless `use_spire_svid` is enabled |
49+
| `client_ca_file` | `string` | Optional path to the PEM-encoded CA bundle used to verify client certificates for mTLS. Cannot be combined with `authorized_spiffe_ids` |
50+
| `use_spire_svid` | `bool` | When `true`, serve the Prometheus endpoint with the current SPIRE SVID instead of `cert_file` and `key_file` |
51+
| `authorized_spiffe_ids` | `list(string)` | Optional list of SPIFFE IDs allowed to connect to the Prometheus endpoint. Requires SPIRE trust bundles and cannot be combined with `client_ca_file` |
4152

4253
### `DogStatsd`
4354

@@ -64,11 +75,17 @@ Here is a sample configuration:
6475
telemetry {
6576
Prometheus {
6677
port = 9988
67-
#optional TLS for prometheus
78+
# optional TLS for prometheus
6879
tls {
69-
cert_file = "/path/to/cert.pem"
70-
key_file = "/path/to/key.pem"
71-
client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
80+
use_spire_svid = true
81+
authorized_spiffe_ids = [
82+
"spiffe://example.org/monitoring/prometheus",
83+
]
84+
85+
# Alternatively, configure a web certificate directly:
86+
# cert_file = "/path/to/cert.pem"
87+
# key_file = "/path/to/key.pem"
88+
# client_ca_file = "/path/to/ca.pem" # optional CA file for mTLS
7289
}
7390
}
7491

pkg/agent/agent.go

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313

1414
"github.com/andres-erbsen/clock"
1515
"github.com/sirupsen/logrus"
16+
"github.com/spiffe/go-spiffe/v2/spiffeid"
17+
"github.com/spiffe/go-spiffe/v2/svid/x509svid"
1618
"github.com/spiffe/go-spiffe/v2/workloadapi"
1719
admin_api "github.com/spiffe/spire/pkg/agent/api"
1820
node_attestor "github.com/spiffe/spire/pkg/agent/attestor/node"
@@ -79,11 +81,45 @@ func (a *Agent) Run(ctx context.Context) error {
7981
defer stopProfiling()
8082
}
8183

84+
var mgr manager.Manager
8285
metrics, err := telemetry.NewMetrics(&telemetry.MetricsConfig{
8386
FileConfig: a.c.Telemetry,
8487
Logger: a.c.Log.WithField(telemetry.SubsystemName, telemetry.Telemetry),
8588
ServiceName: telemetry.SpireAgent,
8689
TrustDomain: a.c.TrustDomain.Name(),
90+
GetX509SVID: func() (*x509svid.SVID, error) {
91+
if mgr == nil {
92+
return nil, errors.New("agent manager is not initialized")
93+
}
94+
95+
credentials := mgr.GetCurrentCredentials()
96+
if len(credentials.SVID) == 0 {
97+
return nil, errors.New("no certificates found")
98+
}
99+
100+
id, err := x509svid.IDFromCert(credentials.SVID[0])
101+
if err != nil {
102+
return nil, err
103+
}
104+
105+
return &x509svid.SVID{
106+
ID: id,
107+
Certificates: credentials.SVID,
108+
PrivateKey: credentials.Key,
109+
}, nil
110+
},
111+
GetX509BundleAuthorities: func(td spiffeid.TrustDomain) ([]*x509.Certificate, error) {
112+
if mgr == nil {
113+
return nil, errors.New("agent manager is not initialized")
114+
}
115+
116+
bundle := mgr.GetBundles()[td]
117+
if bundle == nil {
118+
return nil, fmt.Errorf("no bundle found for trust domain %q", td)
119+
}
120+
121+
return bundle.X509Authorities(), nil
122+
},
87123
})
88124
if err != nil {
89125
return err
@@ -108,8 +144,6 @@ func (a *Agent) Run(ctx context.Context) error {
108144
}
109145

110146
taskRunner := util.NewTaskRunner(ctx, cancel)
111-
taskRunner.StartTasks(metrics.ListenAndServe)
112-
113147
nodeAttestor := nodeattestor.JoinToken(a.c.Log, a.c.JoinToken)
114148
if a.c.JoinToken == "" {
115149
nodeAttestor = cat.GetNodeAttestor()
@@ -226,7 +260,7 @@ func (a *Agent) Run(ctx context.Context) error {
226260

227261
svidStoreCache := a.newSVIDStoreCache(metrics)
228262

229-
manager, err := a.newManager(ctx, sto, cat, metrics, as, svidStoreCache, nodeAttestor)
263+
mgr, err = a.newManager(ctx, sto, cat, metrics, as, svidStoreCache, nodeAttestor)
230264
if err != nil {
231265
return err
232266
}
@@ -238,21 +272,22 @@ func (a *Agent) Run(ctx context.Context) error {
238272
Metrics: metrics,
239273
})
240274

241-
agentEndpoints := a.newEndpoints(metrics, manager, workloadAttestor)
275+
agentEndpoints := a.newEndpoints(metrics, mgr, workloadAttestor)
242276
go func() {
243277
agentEndpoints.WaitForListening(readyForHealthChecks)
244278
a.started = true
245279
}()
246280

247281
tasks := []func(context.Context) error{
248-
manager.Run,
282+
metrics.ListenAndServe,
283+
mgr.Run,
249284
storeService.Run,
250285
agentEndpoints.ListenAndServe,
251286
catalog.ReconfigureTask(a.c.Log.WithField(telemetry.SubsystemName, "reconfigurer"), cat),
252287
}
253288

254289
if a.c.AdminBindAddress != nil {
255-
adminEndpoints := a.newAdminEndpoints(metrics, manager, workloadAttestor, a.c.AuthorizedDelegates)
290+
adminEndpoints := a.newAdminEndpoints(metrics, mgr, workloadAttestor, a.c.AuthorizedDelegates)
256291
tasks = append(tasks, adminEndpoints.ListenAndServe)
257292
}
258293

pkg/agent/manager/manager.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ type Manager interface {
9494

9595
// GetBundle get latest cached bundle
9696
GetBundle() *cache.Bundle
97+
98+
// GetBundles gets the latest cached bundles for all trust domains.
99+
GetBundles() map[spiffeid.TrustDomain]*cache.Bundle
97100
}
98101

99102
// Cache stores each registration entry, signed X509-SVIDs for those entries,
@@ -104,6 +107,9 @@ type Cache interface {
104107
// Bundle gets latest cached bundle
105108
Bundle() *spiffebundle.Bundle
106109

110+
// Bundles gets the latest cached bundles for all trust domains.
111+
Bundles() map[spiffeid.TrustDomain]*spiffebundle.Bundle
112+
107113
// SyncSVIDsWithSubscribers syncs SVID cache
108114
SyncSVIDsWithSubscribers()
109115

@@ -430,6 +436,13 @@ func (m *manager) GetBundle() *cache.Bundle {
430436
return m.cache.Bundle()
431437
}
432438

439+
func (m *manager) GetBundles() map[spiffeid.TrustDomain]*cache.Bundle {
440+
m.mtx.RLock()
441+
defer m.mtx.RUnlock()
442+
443+
return m.cache.Bundles()
444+
}
445+
433446
func (m *manager) runSVIDObserver(ctx context.Context) error {
434447
svidStream := m.SubscribeToSVIDChanges()
435448
for {

pkg/common/telemetry/config.go

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package telemetry
22

33
import (
4+
"crypto/x509"
5+
46
"github.com/hashicorp/hcl/hcl/token"
57
"github.com/sirupsen/logrus"
8+
"github.com/spiffe/go-spiffe/v2/spiffeid"
9+
"github.com/spiffe/go-spiffe/v2/svid/x509svid"
610
)
711

812
type MetricsConfig struct {
9-
FileConfig FileConfig
10-
Logger logrus.FieldLogger
11-
ServiceName string
12-
Sinks []Sink
13-
TrustDomain string
13+
FileConfig FileConfig
14+
Logger logrus.FieldLogger
15+
ServiceName string
16+
Sinks []Sink
17+
TrustDomain string
18+
GetX509SVID func() (*x509svid.SVID, error)
19+
GetX509BundleAuthorities func(spiffeid.TrustDomain) ([]*x509.Certificate, error)
1420
}
1521

1622
type FileConfig struct {
@@ -44,9 +50,11 @@ type PrometheusConfig struct {
4450
}
4551

4652
type TLSConfig struct {
47-
CertFile string `hcl:"cert_file"`
48-
KeyFile string `hcl:"key_file"`
49-
ClientCAFile string `hcl:"client_ca_file"` // optional
53+
CertFile string `hcl:"cert_file"`
54+
KeyFile string `hcl:"key_file"`
55+
ClientCAFile string `hcl:"client_ca_file"` // optional
56+
UseSPIRESVID bool `hcl:"use_spire_svid"`
57+
AuthorizedSPIFFEIDs []string `hcl:"authorized_spiffe_ids"`
5058
}
5159

5260
type StatsdConfig struct {

0 commit comments

Comments
 (0)