forked from spiffe/spike
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjira.xml
More file actions
1488 lines (1335 loc) · 49.9 KB
/
Copy pathjira.xml
File metadata and controls
1488 lines (1335 loc) · 49.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" ?>
<!--
# \\ SPIKE: Secure your secrets with SPIFFE.
# \\\\\ Copyright 2024-present SPIKE contributors.1
# \\\\\\\ SPDX-License-Identifier: Apache-2.0
-->
<stuff>
<purpose>
<target>Our goal is to have a minimally delightful product.</target>
<target>Strive not to add features just for the sake of adding
features.
</target>
<target>Half-assed features shall be completed before adding more
features.
</target>
<meta>
Minimally Delightful Product Requirements:
- A Kubernetes SPIKE deployment
✅ Minimal policy enforcement
- Minimal integration tests
✅ A demo workload that uses SPIKE to test things out as a consumer.
✅ A golang SDK
</meta>
</purpose>
<low-hanging-fruits>
<issue>
if crashed and trying to restore (instead of recover)
you get this:
spike (main)$ spike operator recover
2025/02/24 21:30:35 recover: Problem parsing response body
unexpected end of JSON input
error could be more explanatory
</issue>
<issue>
spike (main)$ spike operator restore
(your input will be hidden as you paste/type it)
Enter recovery shard: 2025/02/24 22:09:34 recover: Problem parsing
response body
unexpected end of JSON input
^ this happens when the input is invalid; a more explanatory error message
would be great.
</issue>
<issue>
// TDO: Yes memory is the source of truth; but at least
// attempt some exponential retries before giving up.
if err := be.StoreSecret(ctx, path, *secret); err != nil {
// Log error but continue - memory is the source of truth
log.Log().Warn(fName,
"msg", "Failed to cache secret",
"path", path,
"err", err.Error(),
)
}
SQLLite can error out if there is a blocked transaction or
a integrity issue, which a retry can fix it.
</issue>
<issue>
sanitize perms
err := api.CreatePolicy(name, spiffeIddPattern, pathPattern, perms)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
</issue>
<issue>
return cobra.Command{
Use: "delete policy-id",
Short: "Delete a policy",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
api := spike.NewWithSource(source)
// TOD: sanitize policy id.
// also validate other command line arguments too if it makes sense.
// better to stop bad data at the client (but still not trust the
// client fully)
Go through all cobra commands and validate/sanitize what needs to
be.
</issue>
</low-hanging-fruits>
<feature title="Security">
<issue>
Initialize the root key with 32 bits of zeroes.
Initializing rootKey with 32 bytes of zeroes (make([]byte, 32)) instead
of letting it dynamically grow will be a better practice from a security
perspective for several reasons:
* Prevents Length Leaks: If we allow the slice to dynamically grow, an
attacker observing memory patterns might infer key-related operations
based on the length changes. By pre-allocating 32 bytes, the length
remains constant.
* Ensures Expected Size: It enforces that the encryption key will always
have a fixed size, reducing the risk of unexpected behavior or bugs
where the key is shorter or longer than expected.
* Avoids Accidental nil Access: Without initialization, rootKey defaults
to nil, which can lead to runtime panics if accessed before being
properly assigned.
* Predictable Memory Layout: Pre-allocating avoids potential fragmentation
in memory allocation, making access patterns more predictable.
Or even better, use an array `rootKey [32]byte` since size is fixed.
When doing it, we'd need to check everywhere rootKey is used because
there are nil check which will regress when using an array or a
non-nil zeroed-out slice.
</issue>
</feature>
<feature title="Containerization">
<issue>
Next prio is likely containerization.
* start by creating dockerhub repos.
* also containerize things locally.
</issue>
</feature>
<low-hanging-fruits>
<issue>
go runtime update to the recent.
update SPIRE too while you are at it.
</issue>
<issue>
try getent in checkdomain
check_domain() {
# Use getent instead of dig as it respects /etc/hosts
if getent hosts "$SPIRE_SERVER_DOMAIN" > /dev/null; then
# Get the full answer for display
DNS_ANSWER=$(getent hosts "$SPIRE_SERVER_DOMAIN")
# Print the resolved address(es)
echo "DNS resolution for $SPIRE_SERVER_DOMAIN:"
echo "$DNS_ANSWER"
return 0
else
echo "Error: No valid DNS answer for $SPIRE_SERVER_DOMAIN"
return 1
fi
}
</issue>
<issue>
To docs:
additional comments.
wrt `mlock`;
* it requires root privileges or `CAP_IPC_LOCK` in Linux.
* there is no windows equivalent.
however without `mlock` if swap is on, zeroing out memory won't be good enough as we won't have any control ove whatever is written on swap.
--
@kfox1111 has suggested to add "turning swap off" to the [production guide](https://spike.ist/operations/production/) as a recommendation.
Turning swap off is recommended for bare-metal k8s control plane and worker nodes anyway; so chances are, it will be turned off if a modern containerized (doker, k8s, etc) environment.
If turning swap is not an option, encrypting swap could be another option too.
</issue>
<issue>
may want to put a note in docs or something to run with swap off.
1:17
not sure if the sanitation bits would work with swap on a system.
^
production guides.
^
also describe how enabling swap might impact the security posture.
</issue>
<issue>
use Mlock optionally
Also we can either "try and shrug" using `mlock` as in
```go
err := unix.Mlock(data)
if err != nil {
// do nothing
}
// Zero out memory
for i := range data {
data[i] = 0
}
```
Or we can put the mlock option behind a feature flag.
If the user has granted `CAP_IPC_LOCK` to SPIKE Nexus, then they can also set something like `SPIKE_NEXUS_USE_MLOCK="true"` as an env var, and try locking memory if the variable is set (will be "false" by default).
https://github.com/spiffe/spike/issues/68
</issue>
<issue>
sanitize keeper id and shard
request := net.HandleRequest[
reqres.ShardContributionRequest, reqres.ShardContributionResponse](
requestBody, w,
reqres.ShardContributionResponse{Err: data.ErrBadInput},
)
if request == nil {
return errors.ErrParseFailure
}
shard := request.Shard
id := request.KeeperId
</issue>
<issue>
validate spiffe id and other parameters
for this and also other keeper endpoints
func RouteShard(
w http.ResponseWriter, r *http.Request, audit *log.AuditEntry,
) error {
const fName = "routeContribute"
log.AuditRequest(fName, r, audit, log.AuditCreate)
requestBody := net.ReadRequestBody(w, r)
if requestBody == nil {
return errors.ErrReadFailure
}
here is an example that does that:
func RoutePutPolicy(
w http.ResponseWriter, r *http.Request, audit *log.AuditEntry,
) error {
const fName = "routePutPolicy"
log.AuditRequest(fName, r, audit, log.AuditCreate)
requestBody := net.ReadRequestBody(w, r)
if requestBody == nil {
return errors.ErrParseFailure
}
request := net.HandleRequest[
reqres.PolicyCreateRequest, reqres.PolicyCreateResponse](
requestBody, w,
reqres.PolicyCreateResponse{Err: data.ErrBadInput},
)
if request == nil {
return errors.ErrReadFailure
}
err := guardPutPolicyRequest(*request, w, r)
if err != nil {
return err
}
</issue>
<issue>
something similar for SPIKE too:
Dev mode
The Helm chart may run a OpenBao server in development. This
installs a
single OpenBao server with a memory storage backend.
For dev mode:
- no keepers
- no backing store (everything is in memory)
</issue>
<issue>
Ensure the system works w/o keepers in "in memory" mode.
also document it and also create a video out of it.
</issue>
<issue>
this should be configurable:
ticker := time.NewTicker(5 * time.Minute)
</issue>
<issue>
nil check wherever Backend() is called.
var be backend.Backend
</issue>
<issue kind="containerization">
GitHub has now arm64 runners. we can use it for
cross-compilation/automation.
https://github.blog/changelog/2025-01-16-linux-arm64-hosted-runners-now-available-for-free-in-public-repositories-public-preview/
Here's an example:
https://github.com/kfox1111/cid2pid/blob/main/.github/workflows/release.yaml
Also, we can (at least temporarily) stop bundling for Mac OS.
Also, worth checking if ARM linux binaries work on Mac.
</issue>
</low-hanging-fruits>
<later>
<issue>
newSecreteUndeleteCommand
Run: func(cmd *cobra.Command, args []string) {
// O: we can pass this as a predicate to newSecretUndeleteCommand,
as a HOF.
trust.Authenticate(spiffeId)
</issue>
<issue>
add retries to everything under:
app/nexus/internal/state/persist
^ they all talk to db; and sqlite can temporarily lock for
a variety of reasons.
</issue>
<issue priority="important" severity="medium">
if a keeper crashes it has to wait for the next nexus cycle which is
suboptimal. Instead, nexus can send a ping that returns an overall
status
of keeper (i.e. if it's populated or not)
this can be more frequent than hydration; and once nexus realizes
keeper
is down, it can rehydrate it.
in addition; nexus can first check the sha hash of the keeper's shard.
before resending; if the hashes match, it won't restransmit the shard.
</issue>
<issue>
path sanitization:
^(?!/)([a-zA-Z0-9._-]+)(/[a-zA-Z0-9._-]+)*$
Hashi Vault is not as script as the above regex; but I think this
gives a nice balance.
^(?!/) → Ensures the path does not start with / (Vault does not require a leading /).
([a-zA-Z0-9._-]+) → The first segment must consist of alphanumeric characters, dots, underscores, or dashes.
(/[a-zA-Z0-9._-]+)* → Subsequent segments must follow the same pattern and be separated by /.
No double slashes (//) allowed.
No spaces, backslashes, or reserved URL characters allowed.
Example Valid Paths:
✅ secret/myapp/config
✅ secrets/db-creds/admin-user
✅ tenantA/projectX/env1/key
Example Invalid Paths:
❌ /secret/myapp/config (leading /)
❌ secret//double-slash (double /)
❌ secret\path (backslash used)
❌ secret path/with space (contains spaces)
❌ secret#invalid?path (reserved URL characters)
</issue>
<issue>
this is for policy creation:
allowed := state.CheckAccess(
spiffeid.String(), "*",
[]data.PolicyPermission{data.PermissionSuper},
)
instead of a wildcard, maybe have a predefined path
for access check like "/spike/system/acl"
also disallow people creating secrets etc under
/spike/system
</issue>
<issue>
path is still stored plain in DB; use HMAC instead.
CREATE TABLE "secrets" (
"hashed_path" BLOB NOT NULL,
"version" INTEGER NOT NULL,
"nonce" BLOB NOT NULL,
"encrypted_data" BLOB NOT NULL,
"created_time" DATETIME NOT NULL,
"deleted_time" DATETIME,
PRIMARY KEY("hashed_path","version")
);
same is true for policies; we need to keep them encrypted too.
</issue>
<issue>
Also, create a script in ./hack that does that.
(i.e. something that forces Nexus to reset its root key
upon next restart; and see how it impacts the system
see SQLite db with a db viewer
or maybe let that script delete the database too.)
// TODO: if you stop nexus, delete the tombstone file, and
restart nexus,
// (and no keeper returns a shard and returns 404)
// it will reset its root key and update the keepers to store
the new
// root key. This is not an attack vector, because an adversary
who can
// delete the tombstone file, can also delete the backing store.
/// Plus no sensitive data is exposed; it's just all data is
inaccessible
// now because the root key is lost for good. In either
// case, for production systems, the backing store needs to be
backed up
// and the root key needs to be backed up in a secure place too.
// ^ add these to the documentation.
</issue>
<issue>
spike operator reset:
deletes and recreates the ~/.spike folder
restarts the initialization flow to rekey keepers.
volkan@spike:~/Desktop/WORKSPACE/spike$ spike secret get /db
Error reading secret: post: Problem connecting to peer
^ I get an error instead of a "secret not found" message.
</issue>
<issue>
verify if the keeper has shard before resending it:
send hash of the shard first
if keeper says “I have it”, don’t send the actual shard.
this will make things extra secure.
</issue>
<issue>
Fleet management:
- There is a management plane cluster
- There is a control plane cluster
- There are workload clusters connected to the control plane
- All of those are their own trust domains.
- There is MP-CP connectivity
- There is CP-WL connectivity
- MP has a central secrets store
- WL and CP need secrets
- Securely dispatch them without "ever" using Kubernetes secrets.
- Have an alternative that uses ESO and a restricted secrets
namespace
that no one other than SPIKE components can see into.
</issue>
<issue>
Wrt DR:
A 404 response from a keeper meens that it does not have the shard
if threshold number of keepers do not have the shard too, then there is
no way that nexus can recover; so does it mean it should stop polling?
Not quite. Because later, we can have ways to seed keepers, maybe through
other nexuses, maybe via cloning from a backup keeper, maybe using a
secure keeper API (i.e. imitating nexus)
It's better, and simpler to keep the polling always running.
It makes less assumptions that way.
make this an ADR.
</issue>
<issue>
configure SPIKE to rekey itself as per NIST guidelines.
Also maybe `spike operator rekey` to manually initiate that.
`spike operator rekey` will also change the shamir shares, wheras the
internal rekey will just change the encryption key, leaving the shamir
shares intact.
</issue>
<issue>
This is OpenBao's production deployment checlist; check of if any of
those also applies for SPIKE, of if we need different/additional items
for SPIKE.
From the context, OpenBao can be swapped with SPIKE Nexus, I think:
End-to-End TLS. OpenBao should always be used with TLS in production. If
intermediate load balancers or reverse proxies are used to front OpenBao,
they should not terminate TLS. This way traffic is always encrypted in
transit to OpenBao and minimizes risks introduced by intermediate layers.
Single Tenancy. OpenBao should be the only main process running on a
machine. This reduces the risk that another process running on the same
machine is compromised and can interact with OpenBao. This can be
accomplished by using OpenBao Helm's affinity configurable.
Enable Auditing. OpenBao supports several auditing backends. Enabling
auditing provides a history of all operations performed by OpenBao and
provides a forensics trail in the case of misuse or compromise. Audit logs
securely hash any sensitive data, but access should still be restricted to
prevent any unintended disclosures. OpenBao Helm includes a configurable
auditStorage option that provisions a persistent volume to store audit
logs.
Immutable Upgrades. OpenBao relies on an external storage backend for
persistence, and this decoupling allows the servers running OpenBao to be
managed immutably. When upgrading to new versions, new servers with the
upgraded version of OpenBao are brought online. They are attached to the
same shared storage backend and unsealed. Then the old servers are
destroyed. This reduces the need for remote access and upgrade
orchestration which may introduce security gaps. See the upgrade section
for instructions on upgrading OpenBao on Kubernetes.
Upgrade Frequently. OpenBao is actively developed, and updating frequently
is important to incorporate security fixes and any changes in default
settings such as key lengths or cipher suites. Subscribe to the OpenBao
mailing list and GitHub CHANGELOG for updates.
Restrict Storage Access. OpenBao encrypts all data at rest, regardless of
which storage backend is used. Although the data is encrypted, an attacker
with arbitrary control can cause data corruption or loss by modifying or
deleting keys. Access to the storage backend should be restricted to only
OpenBao to avoid unauthorized access or operations.
</issue>
<issue>
a mode that enables the admin to load shares to keepers.
this will be essentially using the keeper REST API and acting as
SPIKE Pilot with a recover svid.
The benefit would be; we can have a dedicated recover and restore binaries
without having to expose the pilot binary.
Or we can update the keepers, even if we don't have access to SPIKE Nexus
or SPIKE Nexus is down.
maybe by using a "seeder" SPIFFE ID.
but I'm also not sure if it's worth it.
it would mean more APIs to secure; it would also mean keeping spike
keepers more intelligent (instead of keeping them dumb)
</issue>
<issue>
A /stats endpoint.
A dedicated /stats endpoint will be implemented to provide real-time
metrics about:
Total number of secrets managed.
Status of the key-value store.
Resource utilization metrics (e.g., CPU, memory).
This endpoint will support integration with monitoring tools for enhanced
observability.
These measures will ensure comprehensive monitoring and troubleshooting.
</issue>
<issue>
By design, we regard memory as the source of truth.
This means that backing store might miss some secrets.
Find ways to reduce the likelihood of this happening.
1. Implement exponential retries.
2. Implement a health check to ensure backing store is up.
3. Create background jobs to sync the backing store.
</issue>
<issue>
all components shall have
liveness and readiness endpoints
(or maybe we can design it once we k8s...ify things.
</issue>
<issue>
in development mode, nexus shall act as a single binary:
- you can create secrets and policies via `nexus create policy` etc
that can be done by sharing
"github.com/spiffe/spike/app/spike/internal/cmd"
between nexus and pilot
this can even be an optional flag on nexus
(i.e. SPIKE_NEXUS_ENABLE_PILOT_CLI)
running ./nexus will start a server
but run
ning nexus with args will register secrets and policies.
</issue>
<issue>
Consider using OSS Security Scorecard:
https://github.com/vmware-tanzu/secrets-manager/security/code-scanning/tools/Scorecard/status
</issue>
<issue>
SPIKE automatic rotation of encryption key.
the shards will create a root key and the root key will encrypt the
encryption key.
so SPIKE can rotate the encryption key in the background and encrypt
it with the new root key.
this way, we won't have to rotate the shards to rotate the
encryption key.
</issue>
<issue>
SPIKE CSI Driver
the CSI Secrets Store driver enables users to create
`SecretProviderClass` objects. These objects define which secret
provider
to use and what secrets to retrieve. When pods requesting CSI
volumes are
made, the CSI Secrets Store driver sends the request to the OpenBao
CSI
provider if the provider is `vault`. The CSI provider then uses the
specified `SecretProviderClass` and the pod’s service account to
retrieve
the secrets from OpenBao and mount them into the pod’s CSI volume.
Note
that the secret is retrieved from SPIKE Nexus and populated to the
CSI
secrets store volume during the `ContainerCreation` phase.
Therefore, pods
are blocked from starting until the secrets are read from SPIKE and
written to the volume.
</issue>
<issue>
shall we implement rate limiting; or should that be out of scope
(i.e. to be implemented by the user.
</issue>
<issue>
more fine grained policy management
1. an explicit deny will override allows
2. have allowed/disallowed/required parameters
3. etc.
# This section grants all access on "secret/*". further restrictions
can be
# applied to this broad policy, as shown below.
path "secret/*" {
capabilities = ["create", "read", "update", "patch", "delete",
"list", "scan"]
}
# Even though we allowed secret/*, this line explicitly denies
# secret/super-secret. this takes precedence.
path "secret/super-secret" {
capabilities = ["deny"]
}
# Policies can also specify allowed, disallowed, and required
parameters. here
# the key "secret/restricted" can only contain "foo" (any value) and
"bar" (one
# of "zip" or "zap").
path "secret/restricted" {
capabilities = ["create"]
allowed_parameters = {
"foo" = []
"bar" = ["zip", "zap"]
}
but also, instead of going deep down into the policy rabbit hole,
maybe
it's better to rely on well-established policy engines like OPA.
A rego-based evaluation will give allow/deny decisions, which SPIKE
Nexus
can then honor.
Think about pros/cons of each approach. -- SPIKE can have a
good-enough
default policy engine, and for more sophisticated functionality we
can
leverage OPA.
</issue>
<issue>
key rotation
NIST rotation guidance
Periodic rotation of the encryption keys is recommended, even in the
absence of compromise. Due to the nature of the AES-256-GCM
encryption
used, keys should be rotated before approximately 232
encryptions have been performed, following the guidelines of NIST
publication 800-38D.
SPIKE will automatically rotate the backend encryption key prior to
reaching
232 encryption operations by default.
also support manual key rotation
</issue>
<issue>
Do an internal security analysis / threat model for spike.
</issue>
<issue>
TODO in-memory "dev mode" for SPIKE #spike (i.e. in memory mode will
not be default)
nexus --dev or something similar (maybe an env var)
</issue>
<issue>
Use SPIKE in lieu of encryption as a service (similar to transit
secrets)
</issue>
<issue>
dynamic secrets
</issue>
<issue>
use case:
one time access to an extremely limited subset of secrets
(maybe using a one time, or time-bound token)
but also consider if SPIKE needs tokens at all; I think we can
piggyback
most of the authentication to SPIFFE and/or JWT -- having to convert
various kinds of tokens into internal secrets store tokens is not
that much needed.
</issue>
<issue>
- TODO Telemetry
- core system metrics
- audit log metrics
- authentication metrics
- database metrics
- policy metrics
- secrets metrics
</issue>
<issue>
"token" secret type
- will be secure random
- will have expiration
</issue>
<issue>
double-encryption of nexus-keeper comms (in case mTLS gets
compromised, or
SPIRE is configured to use an upstream authority that is
compromised, this
will provide end-to-end encryption and an additional layer of
security
over
the existing PKI)
</issue>
<issue>
* Implement strict API access controls:
* Use mTLS for all API connections
* Enforce SPIFFE-based authentication
* Implement rate limiting to prevent brute force attacks
* Configure request validation:
* Validate all input parameters
* Implement request size limits
* Set appropriate timeout values
* Audit API usage:
* Log all API requests
* Monitor for suspicious patterns
* Regular review of API access logs
----
* Enable comprehensive auditing:
* Log all secret access attempts
* Track configuration changes
* Monitor system events
* Implement compliance controls:
* Regular compliance checks
* Documentation of security controls
* Periodic security assessments
---
* Tune for security and performance:
* Optimize TLS session handling
* Configure appropriate connection pools
* Set proper cache sizes
* Monitor performance metrics:
* Track response times
* Monitor error rates
* Alert on performance degradation
</issue>
<issue>
ability to clone a keeper cluster to another standby keeper cluster
(for redundancy).
this way, if the set of keepers become not operational, we can
hot-switch to the other keeper cluster.
the assumption here is the redundant keeper cluster either remains
healthy, or is somehow snapshotted -- since the shards are in
memory, snapshotting will be hard. -- but stil it's worth thinking
about.
an alternative option would be to simplyh increase the number of
keepers.
</issue>
<issue>
ErrPolicyExists = errors.New("policy already exists")
^ this error is never used; check why.
</issue>
<issue>
work on the "named admin" feature (using Keycloak as an OIDC
provider)
This is required for "named admin" feature.
</issue>
<issue>
BootstrapOrDie()
if we are certain that SPIKE nexus cannot bootstrap, maybe it can
just kill itself.
</issue>
<issue>
Consider using google kms, azure keyvault, and other providers
(including an external SPIKE deployment) for root key recovery.
question to consider is whether it's really needed
second question to consider is what to link kms to (keepers or
nexus?)
keepers would be better because we'll back up the shards only then.
or google kms can be used as an alternative to keepers
(i.e., store encrypted dek, with the encrypted root key on nexus;
only kms can decrypt it -- but, to me, it does not provide any
additional advantage since if you are on the machine, you can talk
to
google kms anyway)
</issue>
<issue>
dev mode with "zero" keepers.
</issue>
<issue>
remove symbols when packaging binaries for release.
</issue>
<issue severity="important" priority="above-normal">
consider db backend as untrusted
i.e. encrypt everything you store there; including policies.
(that might already be the case actually) -- if so, document it
in the website.
</issue>
<issue>
exponentially back off here
log.Log().Info("tick", "msg", "Waiting for keepers to initialize")
time.Sleep(5 * time.Second)
or maybe not; I'm not sure if it's worth the effort.
or maybe this algorithm has changed already; needs to be
double-checked.
</issue>
<issue kind="good-first-issue"
ref="https://github.com/spiffe/spike/issues/80">
validations:
along with the error code, also return some explanatory message
instead of this for example
err = validation.ValidateSpiffeIdPattern(spiffeIdPattern)
if err != nil {
responseBody := net.MarshalBody(reqres.PolicyCreateResponse{
Err: data.ErrBadInput,
}, w)
net.Respond(http.StatusBadRequest, responseBody, w)
return err
}
do this
err = validation.ValidateSpiffeIdPattern(spiffeIdPattern)
if err != nil {
responseBody := net.MarshalBody(reqres.PolicyCreateResponse{
Err: data.ErrBadInput,
Reason: "Invalid spiffe id pattern. Matcher should be a regex that
can match a spiffe id"
}, w)
net.Respond(http.StatusBadRequest, responseBody, w)
return err
}
</issue>
<issue>
control these with flags.
i.e. the starter script can optionally NOT automatically
start nexus or keepers.
#echo ""
#echo "Waiting before SPIKE Keeper 1..."
#sleep 5
#run_background "./hack/start-keeper-1.sh"
#echo ""
#echo "Waiting before SPIKE Keeper 2..."
#sleep 5
#run_background "./hack/start-keeper-2.sh"
#echo ""
#echo "Waiting before SPIKE Keeper 3..."
#sleep 5
#run_background "./hack/start-keeper-3.sh"
#echo ""
#echo "Waiting before SPIKE Nexus..."
#sleep 5
#run_background "./hack/start-nexus.sh"
</issue>
</later>
<runner-up>
<issue>
read policies from a yaml or a json file and create them.
</issue>
<issue>
test that the timeout results in an error.
ctx, cancel := context.WithTimeout(
context.Background(), env.DatabaseOperationTimeout(),
)
defer cancel()
cachedPolicy, err := retry.Do(ctx, func() (*data.Policy, error) {
return be.LoadPolicy(ctx, id)
})
</issue>
<issue>
ability to lock nexus programmatically.
when locked, nexus will deny almost all operations
locking is done by executing nexus binary with a certain command
line flag.
(i.e. there is no API access, you'll need to physically exec the
./nexus
binary -- regular svid verifications are still required)
only a superadmin can lock or unlock nexus.
^ instead of that, you can run a script that removes all SVID
registrations. That will effectively result in the same thing.
</issue>
<issue>
consider using NATS for cross trust boundary (or nor) secret
federation
</issue>
<issue>
over the break, I dusted off
https://github.com/spiffe/helm-charts-hardened/pull/166 and started
playing with the new k8s built in cel based mutation functionality.
the k8s cel support is a little rough, but I was able to do a whole
lot in it, and think I can probably get it to work for everything.
once 1.33 hits, I think it will be even easier.
I mention this, as I think spike may want similar functionality?
csi driver, specify secrets to fetch to volume automatically, keep
it up to date, and maybe poke the process once refreshed
</issue>
<issue>
wrt: secure erasing shards and the root key >>
It would be interesting to try and chat with some of the folks under
the cncf
(That's a good idea indeed; I'm noting it down.)
</issue>
<issue severity="important" urgency="moderate">
// TDO: check all database operations (secrets, policies, metadata)
and
// ensure that they are retried with exponential backooff.
</issue>
<issue>
we need a "reset" command for the restore operation in case
we pushed an incorrect set of shards.
</issue>
<issue>
better play with OIDC and keycloak sometime.
</issue>
<issue>
attribute-based policy control
path "secret/restricted" {
capabilities = ["create"]
allowed_parameters = {
"foo" = []
"bar" = ["zip", "zap"]
}
}
</issue>
<issue>
Note: this is non-trivial, but doable.
Periodic rotation of the encryption keys is recommended, even in the
absence of compromise. Due to the nature of the AES-256-GCM
encryption
used, keys should be rotated before approximately 232 encryptions
have
been performed, following the guidelines of NIST publication
800-38D.
This can be achieved by having a separate encryption key protected
by
the root key and rotating the encryption key, and maybe maintaining
a
keyring. This way, we won't have to rotate shards to rotate the
encryption
key and won't need to change the shards -- this will also allow the
encryption key to be rotated behind-the-scenes automatically as per
NIST guidance.
</issue>
<issue severity="important" priority="elevated">
write an adr about why those asnyc are snyc from now on:
// TOD we don't have any retry for policies or for recovery info.
// they are equally important.
// TO: these xsync operations can cause race conditions
//
// 1. process a writes secret
// 2. process b marks secret as deleted
// 3. in memory we write then delete
// 4. but to the backing store it goes as delete then write.
// 5. memory: secret deleted; backing store: secret exists.
//
// to solve it; have a queue of operations (as a go channel)
// and do not consume the next operation until the current
// one is complete.
//
// have one channel for each resource:
// - secrets
// - policies
// - key recovery info.
//
// Or as an alternative; make these xsync operations sync
// and wait for them to complete before reporting success.
// this will make the architecture way simpler without needing
// to rely on channels.
</issue>
<issue>
the backing store is considered untrusted and it stores
encrypted information
todo: if it's "really" untrusted then maybe it's better to encrypt
everything
(including metadata) -- check how other secrets managers does this.
</issue>
<issue kind="good-first-issue">
create a `spike status` command that shows the current status an
stats
of SPIKE Nexus (i.e. whether root key initialized; how many secrets
are
in the system etc.)
It does not have to be too detailed; we can always amend it later.
</issue>
<issue>
Kubernetification
</issue>
<issue>