mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-02-28 08:44:01 +00:00
Compare commits
849 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a439acc3 | ||
|
|
1254f730f2 | ||
|
|
69281a4620 | ||
|
|
02f7ad2fff | ||
|
|
2e3e0eed8e | ||
|
|
72cea70967 | ||
|
|
1428444c24 | ||
|
|
7276a1df53 | ||
|
|
33c68e2e7d | ||
|
|
0986c90678 | ||
|
|
986ebe0063 | ||
|
|
9b24ecad19 | ||
|
|
2e4f719419 | ||
|
|
f45e0aeb3d | ||
|
|
e743beac53 | ||
|
|
b294c7657a | ||
|
|
524989e16b | ||
|
|
a6b2905c3a | ||
|
|
aa2bdcb8dd | ||
|
|
a315cc4977 | ||
|
|
b8e7a64afe | ||
|
|
b50da60c84 | ||
|
|
dfc4c38259 | ||
|
|
dce1f5b208 | ||
|
|
c600cf7949 | ||
|
|
32a29da86c | ||
|
|
9ac9930650 | ||
|
|
1f01c394f3 | ||
|
|
a41ca92b8e | ||
|
|
f75eef16b4 | ||
|
|
2e801dc275 | ||
|
|
b8b460f98a | ||
|
|
39588c3bb4 | ||
|
|
79035bc2b6 | ||
|
|
85c94116ce | ||
|
|
7e8ea9f1fb | ||
|
|
a0e38fe8c1 | ||
|
|
f9c3d932c1 | ||
|
|
861c501ea8 | ||
|
|
e129a569ba | ||
|
|
fd42da225d | ||
|
|
b139a458fd | ||
|
|
68a2df41ed | ||
|
|
2cbdd60022 | ||
|
|
0e12f90749 | ||
|
|
7e132a5dcd | ||
|
|
910326f1ea | ||
|
|
6057b0c912 | ||
|
|
2845829fb4 | ||
|
|
653b6d5b0d | ||
|
|
7ef58a9132 | ||
|
|
cf98c69c0d | ||
|
|
62f6d8516b | ||
|
|
604ecc583c | ||
|
|
a010794873 | ||
|
|
c7462bf1f6 | ||
|
|
b396d12456 | ||
|
|
695ea3a165 | ||
|
|
d625ada06a | ||
|
|
0f48620d9c | ||
|
|
c2496aab6b | ||
|
|
00734d143e | ||
|
|
d90b3c23ef | ||
|
|
035dbffd28 | ||
|
|
e3710289e5 | ||
|
|
99b67b89ac | ||
|
|
e374841857 | ||
|
|
9f2336476f | ||
|
|
63b560d740 | ||
|
|
f54834cbee | ||
|
|
749633e43c | ||
|
|
308c76cd69 | ||
|
|
fc9a261b77 | ||
|
|
d2d860bd3b | ||
|
|
9cb3638354 | ||
|
|
6133276555 | ||
|
|
a7a3016ae5 | ||
|
|
dd0dcad8c4 | ||
|
|
ed8c9448de | ||
|
|
5e80fb26f0 | ||
|
|
292855332c | ||
|
|
68b0dd83f2 | ||
|
|
611fc61c12 | ||
|
|
2cd4a2c730 | ||
|
|
3e89915a98 | ||
|
|
f4aaee8bd7 | ||
|
|
d56413a289 | ||
|
|
27b629af3a | ||
|
|
1a3cb65fa4 | ||
|
|
4b3eb0e6ec | ||
|
|
f89b13bc62 | ||
|
|
8b7fec5049 | ||
|
|
3a6573f89e | ||
|
|
4e04f5b606 | ||
|
|
39a86e7d52 | ||
|
|
8cfc1c08ec | ||
|
|
c90637398d | ||
|
|
737aabb620 | ||
|
|
0e930b3d63 | ||
|
|
876f626e7d | ||
|
|
0dc704be9c | ||
|
|
e437832698 | ||
|
|
274ca4cb73 | ||
|
|
a99ff646a0 | ||
|
|
02eb26f135 | ||
|
|
e90f19f8ab | ||
|
|
00c2c5cf6e | ||
|
|
3386774f5f | ||
|
|
7e4330be93 | ||
|
|
f5b3e6da93 | ||
|
|
5c39374915 | ||
|
|
4fdb931141 | ||
|
|
3a02854192 | ||
|
|
63c071d6ea | ||
|
|
6dc96f4224 | ||
|
|
aa8f8f7fda | ||
|
|
f5167bb279 | ||
|
|
b84eafc173 | ||
|
|
50ed1b0cf9 | ||
|
|
1d873be184 | ||
|
|
5a0d6eddb1 | ||
|
|
31b45525ce | ||
|
|
430c73b903 | ||
|
|
cc1befbc57 | ||
|
|
68a0ad4112 | ||
|
|
9aca187559 | ||
|
|
d0fb9f3637 | ||
|
|
51d1bc32e8 | ||
|
|
e9b9dd6fa3 | ||
|
|
7e43aa4e12 | ||
|
|
de509db7be | ||
|
|
69c6676d8f | ||
|
|
2ab11dccfc | ||
|
|
d64b4677b9 | ||
|
|
f040f098dc | ||
|
|
abe3391cce | ||
|
|
6ae27c87f6 | ||
|
|
3d2446d235 | ||
|
|
4f01b3157f | ||
|
|
14e728aa0d | ||
|
|
4a266a44db | ||
|
|
900db0d3a3 | ||
|
|
8b95b141b2 | ||
|
|
5a9f2f3181 | ||
|
|
4f43f01e55 | ||
|
|
7221be5a8a | ||
|
|
83ab099b84 | ||
|
|
5f79860c8c | ||
|
|
e4f7b5d181 | ||
|
|
9619a0f226 | ||
|
|
23f414c384 | ||
|
|
4872be0a84 | ||
|
|
691307a269 | ||
|
|
6d846ad2a9 | ||
|
|
ff24c757b7 | ||
|
|
ee4663aa19 | ||
|
|
119c591500 | ||
|
|
275412e902 | ||
|
|
fe75ebe4b2 | ||
|
|
fabb80cf19 | ||
|
|
7577f20c61 | ||
|
|
f441714f93 | ||
|
|
2c9547e6a4 | ||
|
|
3bf3ed03f5 | ||
|
|
c279253e20 | ||
|
|
fa9ddf48d5 | ||
|
|
b8a9c4d1e5 | ||
|
|
b4365c100f | ||
|
|
ef4b0c9cff | ||
|
|
b625b4a076 | ||
|
|
acbe9ce23d | ||
|
|
90c95866d1 | ||
|
|
57fc177266 | ||
|
|
0366f4087f | ||
|
|
3f6d287b44 | ||
|
|
36aa701b56 | ||
|
|
fc5a776645 | ||
|
|
c2b4390bfa | ||
|
|
b371389c27 | ||
|
|
87640ca54a | ||
|
|
8322b03d63 | ||
|
|
594c3580f2 | ||
|
|
0d80c492f1 | ||
|
|
1a29cca1ca | ||
|
|
b54191f29f | ||
|
|
422e4e4785 | ||
|
|
4187cc1f61 | ||
|
|
ede9e45211 | ||
|
|
a36550d94b | ||
|
|
7c3870f3fa | ||
|
|
7ca2796774 | ||
|
|
170cc3bba4 | ||
|
|
1980912ebe | ||
|
|
1571859d67 | ||
|
|
eb4c20a6aa | ||
|
|
1154139b91 | ||
|
|
28e22d7dd2 | ||
|
|
9cfbbb541a | ||
|
|
21bce1cb92 | ||
|
|
fe045343ee | ||
|
|
df017f9267 | ||
|
|
ae5aad178d | ||
|
|
032160a85e | ||
|
|
ecd23e86ce | ||
|
|
51ae782135 | ||
|
|
54b35c30da | ||
|
|
dfe04c5a58 | ||
|
|
4423d472da | ||
|
|
c803a182be | ||
|
|
bc73505e35 | ||
|
|
0a28c818ad | ||
|
|
ce2dcbdbb3 | ||
|
|
1ebe2fcd1a | ||
|
|
c7e9ee1c61 | ||
|
|
51c86795af | ||
|
|
8dffd60f0b | ||
|
|
6bf9b64778 | ||
|
|
26ec7fa346 | ||
|
|
60bd118a9c | ||
|
|
b69507f7f3 | ||
|
|
7d59df0f86 | ||
|
|
9c0272382f | ||
|
|
2de8d9f0f3 | ||
|
|
d0905c02dd | ||
|
|
51fc86f950 | ||
|
|
76bda12760 | ||
|
|
a84b76e56a | ||
|
|
c2018717b6 | ||
|
|
f388513145 | ||
|
|
c16ebe1707 | ||
|
|
b54365c199 | ||
|
|
51d1cc7a96 | ||
|
|
c06141c871 | ||
|
|
eab3fde3af | ||
|
|
de7781b7f9 | ||
|
|
611de03e01 | ||
|
|
de722332b1 | ||
|
|
438ca437ec | ||
|
|
e21e1326b7 | ||
|
|
37e12b4024 | ||
|
|
e126ee5495 | ||
|
|
a308f3f22a | ||
|
|
c5f4cce3ae | ||
|
|
ced8686d11 | ||
|
|
76f6b725b8 | ||
|
|
f9e1dd4bec | ||
|
|
f4f393e5de | ||
|
|
2db5dda266 | ||
|
|
8cf9c59957 | ||
|
|
088556193d | ||
|
|
18d3ab3d15 | ||
|
|
dc6faa33bb | ||
|
|
0d22ae2c1a | ||
|
|
362d982906 | ||
|
|
1006dd9379 | ||
|
|
369316556a | ||
|
|
cf4b29de4b | ||
|
|
09ca7920ea | ||
|
|
9994e033b2 | ||
|
|
dd56f2b47f | ||
|
|
4df043a91c | ||
|
|
d020de4b3d | ||
|
|
dd42f35db0 | ||
|
|
a67af9455b | ||
|
|
d729c82f84 | ||
|
|
44e218194b | ||
|
|
bf1e37f149 | ||
|
|
aee56c388f | ||
|
|
fd5a10bee7 | ||
|
|
b20e890f15 | ||
|
|
4f9530eec7 | ||
|
|
615b60bd37 | ||
|
|
e61afcd109 | ||
|
|
6ac5446940 | ||
|
|
0706681180 | ||
|
|
e44d70b41d | ||
|
|
4bf810cb8f | ||
|
|
c791db4c52 | ||
|
|
e86f3cc594 | ||
|
|
be6243c446 | ||
|
|
4263ee52f3 | ||
|
|
d6f1c91b9c | ||
|
|
105dc4a249 | ||
|
|
aa80c8d0b2 | ||
|
|
7c9bdfb96e | ||
|
|
84b3c0ad31 | ||
|
|
f2538689e7 | ||
|
|
66eb7735dd | ||
|
|
33edb7ea15 | ||
|
|
f3c9be07c0 | ||
|
|
0c131f11f8 | ||
|
|
dc86c9305c | ||
|
|
36ff99882f | ||
|
|
3ed4b1c132 | ||
|
|
8fad2c5127 | ||
|
|
a25749f087 | ||
|
|
248b1ef947 | ||
|
|
feef4bf508 | ||
|
|
aa70ff13f4 | ||
|
|
8a6c64095d | ||
|
|
ea40ffef06 | ||
|
|
44d9dc7440 | ||
|
|
fedb9812bd | ||
|
|
febbee347b | ||
|
|
a7edbd19ad | ||
|
|
c39b2fe03d | ||
|
|
1e23f94b36 | ||
|
|
106a480dad | ||
|
|
587e6fbd8a | ||
|
|
dc2275099a | ||
|
|
bcb9175aa8 | ||
|
|
dd71de9aa1 | ||
|
|
1c4fe6e406 | ||
|
|
697757ba8e | ||
|
|
774df36f41 | ||
|
|
a092b68f61 | ||
|
|
2587b0a8ad | ||
|
|
ff0e849730 | ||
|
|
c25d30ae88 | ||
|
|
51bc70a11b | ||
|
|
d26e54fd89 | ||
|
|
fc6bcc2f5b | ||
|
|
96f0ea2311 | ||
|
|
261f4a4e5b | ||
|
|
f1e933e7aa | ||
|
|
590f001f17 | ||
|
|
88e17c8f86 | ||
|
|
009470883e | ||
|
|
99ad89211a | ||
|
|
67841f8e27 | ||
|
|
eb5ed1490c | ||
|
|
46182f03b3 | ||
|
|
e37d1444c4 | ||
|
|
4b1577ab70 | ||
|
|
c235239ee7 | ||
|
|
f36298c542 | ||
|
|
c3afa55738 | ||
|
|
5c252fd083 | ||
|
|
dc195536d0 | ||
|
|
4d2bbac674 | ||
|
|
eca8914760 | ||
|
|
2aa30edb88 | ||
|
|
4f661aaa69 | ||
|
|
60cfa470b5 | ||
|
|
7a41b74ac1 | ||
|
|
b400648e56 | ||
|
|
d4bb12ec6d | ||
|
|
6fe55a3b48 | ||
|
|
3e72e40af2 | ||
|
|
2501d41d93 | ||
|
|
629f89d95b | ||
|
|
bb00587bd7 | ||
|
|
973886b683 | ||
|
|
4e30bc37f1 | ||
|
|
01c2377de0 | ||
|
|
0fab37c089 | ||
|
|
f7fd209f29 | ||
|
|
76a116641f | ||
|
|
1e0f7d8437 | ||
|
|
7295ec661f | ||
|
|
1a1aac7d09 | ||
|
|
ab2c2e30cb | ||
|
|
65b93f0822 | ||
|
|
da77fb1c3f | ||
|
|
d9ccb11092 | ||
|
|
e62f9b2b48 | ||
|
|
62d17155ec | ||
|
|
702d5bdc01 | ||
|
|
8ee08050cc | ||
|
|
1d81474d1b | ||
|
|
00e9b347db | ||
|
|
72fa369fc9 | ||
|
|
08abff1cae | ||
|
|
f476259bbf | ||
|
|
c87f091a44 | ||
|
|
d165899870 | ||
|
|
6c97600174 | ||
|
|
3e2f7456c4 | ||
|
|
ca9503e4c0 | ||
|
|
dc72a36cb1 | ||
|
|
18e2024e3f | ||
|
|
7d83e209c8 | ||
|
|
1bbfa4984d | ||
|
|
557dee06f0 | ||
|
|
c0bab69cd1 | ||
|
|
f480daf7dc | ||
|
|
cf56d808f2 | ||
|
|
b78e2c7ded | ||
|
|
9b3bcca15e | ||
|
|
f7f25a8815 | ||
|
|
94809ee396 | ||
|
|
851855d0ee | ||
|
|
2a6a0d2997 | ||
|
|
c6516af31e | ||
|
|
05098c68f6 | ||
|
|
a1dafcf45a | ||
|
|
0ee8ee80e1 | ||
|
|
436112252d | ||
|
|
8bd9b94d0a | ||
|
|
504f0dc26f | ||
|
|
c12402ee49 | ||
|
|
f09b3c2f72 | ||
|
|
90c1f6a8c9 | ||
|
|
844c169bdc | ||
|
|
c1930833eb | ||
|
|
f194594e5b | ||
|
|
5bb4adea30 | ||
|
|
376b83050a | ||
|
|
bee87395b1 | ||
|
|
21ee90ae41 | ||
|
|
d2f00d832e | ||
|
|
764064716f | ||
|
|
7b051ddc22 | ||
|
|
9aac96313a | ||
|
|
fcdb340623 | ||
|
|
4301eb6553 | ||
|
|
649bbc7c4f | ||
|
|
1eb6145080 | ||
|
|
00f6f39b1c | ||
|
|
d0f5c2c7ab | ||
|
|
1cfb83bca9 | ||
|
|
4ef705ae0f | ||
|
|
5e6f6a1c50 | ||
|
|
59c36ee972 | ||
|
|
ae3cfad10b | ||
|
|
21010b000e | ||
|
|
4d2b00f612 | ||
|
|
bab8b54ed8 | ||
|
|
4bd5db14b4 | ||
|
|
4a9136040c | ||
|
|
8b97414f3d | ||
|
|
1e8e9ecc98 | ||
|
|
2d8ab9ff5d | ||
|
|
17f66331ea | ||
|
|
6bf30bc6b5 | ||
|
|
f798777a3b | ||
|
|
6207121c03 | ||
|
|
248994dab6 | ||
|
|
a68db4f0db | ||
|
|
aad6b676b0 | ||
|
|
bf91b2045c | ||
|
|
fcf6ec6731 | ||
|
|
b377040144 | ||
|
|
c1328d9619 | ||
|
|
f918edd846 | ||
|
|
6b49cd7d28 | ||
|
|
afa3aa2232 | ||
|
|
1c59a41cc5 | ||
|
|
0626b22c70 | ||
|
|
fbbec507d1 | ||
|
|
a4b0416174 | ||
|
|
659f33dc55 | ||
|
|
20ddf553ce | ||
|
|
7483de5e90 | ||
|
|
9f1d6258a2 | ||
|
|
99b59a90b6 | ||
|
|
56bf9bad25 | ||
|
|
229b6a262e | ||
|
|
74d9fb863f | ||
|
|
e332fb505c | ||
|
|
dafde586ec | ||
|
|
cb101e4dbe | ||
|
|
6fdfee36fe | ||
|
|
0787301ddb | ||
|
|
2af510a3ee | ||
|
|
fdeca2c026 | ||
|
|
23fd15f840 | ||
|
|
06b7d302a2 | ||
|
|
b70db9dc03 | ||
|
|
d4ac69d88e | ||
|
|
59c2295dfd | ||
|
|
4eb9a09385 | ||
|
|
db2d7c8c50 | ||
|
|
2ebf9d3d00 | ||
|
|
67de14a3b8 | ||
|
|
a40c88ebf3 | ||
|
|
23129da3e2 | ||
|
|
59402bca7b | ||
|
|
c3405095b2 | ||
|
|
2181418cc5 | ||
|
|
e0235ed190 | ||
|
|
02e41baa47 | ||
|
|
91ef68992c | ||
|
|
43964ff7a2 | ||
|
|
19c4acf391 | ||
|
|
ed502949dd | ||
|
|
a0c259ffbc | ||
|
|
d6d66faae3 | ||
|
|
15c84fcc94 | ||
|
|
1438f06c12 | ||
|
|
ca5bb2170c | ||
|
|
05a2fd97f8 | ||
|
|
dedd51df91 | ||
|
|
290676e4d1 | ||
|
|
8725ab4caa | ||
|
|
3891f90f43 | ||
|
|
9f17ba5ae4 | ||
|
|
81d42cb3b9 | ||
|
|
dfef9f470f | ||
|
|
f5da417450 | ||
|
|
a888083c50 | ||
|
|
99cfc4fbce | ||
|
|
fcceeed9fa | ||
|
|
4cf0e46c38 | ||
|
|
34eff2a2f9 | ||
|
|
e82cb2c7ba | ||
|
|
0711093ccd | ||
|
|
15d0006841 | ||
|
|
282b949c24 | ||
|
|
005dbf3aa8 | ||
|
|
a1dcba4731 | ||
|
|
2a62beeb5f | ||
|
|
242fa8afb2 | ||
|
|
e3ed722252 | ||
|
|
9a16dc28b7 | ||
|
|
de86809b69 | ||
|
|
9420bfde5b | ||
|
|
adb460b644 | ||
|
|
06b47a5792 | ||
|
|
ca2dd2d476 | ||
|
|
60f82d2a55 | ||
|
|
414ff503ef | ||
|
|
4ec5766ea9 | ||
|
|
b7c26c43ca | ||
|
|
4b2ed52f44 | ||
|
|
f381c92f0b | ||
|
|
3a303cc8fb | ||
|
|
09724cfa71 | ||
|
|
d74c2a6e3f | ||
|
|
0f9352db3b | ||
|
|
afec420ce6 | ||
|
|
d5e3ad9da0 | ||
|
|
0f103ed2a4 | ||
|
|
d62d6a1f27 | ||
|
|
a4ad5d68a9 | ||
|
|
30c0fd479e | ||
|
|
756966c55b | ||
|
|
288e092d2e | ||
|
|
72745cd8fe | ||
|
|
8060e82745 | ||
|
|
373713f7e0 | ||
|
|
66401b42d8 | ||
|
|
2d5943b21a | ||
|
|
920b519ebf | ||
|
|
bf1c02d328 | ||
|
|
6e9023e090 | ||
|
|
1b7a26d932 | ||
|
|
cb4b63f8b3 | ||
|
|
8eb15a924f | ||
|
|
6a610a9d51 | ||
|
|
821a893f70 | ||
|
|
afcd80de37 | ||
|
|
edc327ba33 | ||
|
|
90e8cc86c2 | ||
|
|
9ab7c39d56 | ||
|
|
207bac9452 | ||
|
|
199562fd05 | ||
|
|
3a969a83b7 | ||
|
|
aab1ee9edc | ||
|
|
080c75efe6 | ||
|
|
7e6dadb508 | ||
|
|
19c3f2cb04 | ||
|
|
842f14af4c | ||
|
|
05c258026a | ||
|
|
1bb38911dc | ||
|
|
ec943fffdc | ||
|
|
ae1d182b30 | ||
|
|
82c056b955 | ||
|
|
7acc2aa383 | ||
|
|
4e6a39ed11 | ||
|
|
d587c6b10e | ||
|
|
51c5a05ea7 | ||
|
|
f0cac8c5d3 | ||
|
|
76f3430c68 | ||
|
|
0e4f7082b0 | ||
|
|
66f005f275 | ||
|
|
bc10d500b7 | ||
|
|
7c7f0fdae3 | ||
|
|
4fa901c017 | ||
|
|
fd11c37825 | ||
|
|
f1b82dbf1f | ||
|
|
8891455e10 | ||
|
|
5540f25932 | ||
|
|
ee9bbbe50b | ||
|
|
e013c90993 | ||
|
|
fa85be4b94 | ||
|
|
276cba08ee | ||
|
|
0e312c88c1 | ||
|
|
00301e3642 | ||
|
|
a2be4b7b5e | ||
|
|
b5a509f27f | ||
|
|
6b722a14c8 | ||
|
|
dd0c805b09 | ||
|
|
6c35490cfb | ||
|
|
19a04ea804 | ||
|
|
a9a63914b2 | ||
|
|
b7d1c3f5f6 | ||
|
|
a8ccdbc833 | ||
|
|
decf1cf537 | ||
|
|
e5cfa521da | ||
|
|
dd80627dfa | ||
|
|
f79c844c71 | ||
|
|
cb550dfed0 | ||
|
|
602623a0ba | ||
|
|
6d7646c0a2 | ||
|
|
51518aeb03 | ||
|
|
f3e710c814 | ||
|
|
b132b14982 | ||
|
|
6cd45fa81c | ||
|
|
95e4b8fcdf | ||
|
|
b4cd64e999 | ||
|
|
82dbb93e2c | ||
|
|
b7e12334d6 | ||
|
|
a39eac6f1b | ||
|
|
418ec2a01f | ||
|
|
0380a9ce33 | ||
|
|
ed338d1455 | ||
|
|
10699314d4 | ||
|
|
1f7b6133cd | ||
|
|
3c0ed4d5e3 | ||
|
|
e38f0824dc | ||
|
|
a7d2c50550 | ||
|
|
9fee276214 | ||
|
|
ef7c7d879b | ||
|
|
ea64444c8b | ||
|
|
84308f3518 | ||
|
|
88bcdbadce | ||
|
|
b50d1384dc | ||
|
|
b3457271a9 | ||
|
|
fc6de7e86c | ||
|
|
6b87bc3fa9 | ||
|
|
238df12920 | ||
|
|
f7f32f2f98 | ||
|
|
1f8ac0ff23 | ||
|
|
75ff3efb59 | ||
|
|
f0f9efa277 | ||
|
|
4ab2ed10f5 | ||
|
|
53031ad8d4 | ||
|
|
ce1ad010e9 | ||
|
|
5d6dbe1fc3 | ||
|
|
774504e209 | ||
|
|
2275cd5985 | ||
|
|
3b520d6e93 | ||
|
|
80e1fee208 | ||
|
|
011d6ba71b | ||
|
|
9296d95084 | ||
|
|
c6463831ac | ||
|
|
247ac10215 | ||
|
|
678be9902a | ||
|
|
bafd578866 | ||
|
|
8ac2dc2e9e | ||
|
|
5689bf621c | ||
|
|
ae1cf53dcf | ||
|
|
ced019291b | ||
|
|
b483409c63 | ||
|
|
1fe6f2eff9 | ||
|
|
07f8c327e4 | ||
|
|
e9fa98edfb | ||
|
|
6c102f09f2 | ||
|
|
ea9d06c819 | ||
|
|
05fb8b75ef | ||
|
|
ee2aa9ce50 | ||
|
|
8b1bf8d4f9 | ||
|
|
ca9560effb | ||
|
|
6b64a8a9c6 | ||
|
|
dd3c1ac3cf | ||
|
|
16c8911dfd | ||
|
|
8ea9ba8860 | ||
|
|
1d8decfdad | ||
|
|
57431ce6c2 | ||
|
|
01723e0d12 | ||
|
|
8733a24ebf | ||
|
|
58b4ecc0aa | ||
|
|
b43b8439c4 | ||
|
|
f2747ef4e7 | ||
|
|
e3d8c71f97 | ||
|
|
0ace5cf477 | ||
|
|
6364ac9ac7 | ||
|
|
e89daadfcf | ||
|
|
eee737186f | ||
|
|
c5b54ec27e | ||
|
|
2ad43789cc | ||
|
|
38a90fa01e | ||
|
|
037fa65c63 | ||
|
|
de68594fd6 | ||
|
|
6327f51f5b | ||
|
|
781ee82b9d | ||
|
|
34d02466d9 | ||
|
|
8dd34d9ea9 | ||
|
|
3c0bad9ea2 | ||
|
|
2bf11ffde1 | ||
|
|
bb9cb739c6 | ||
|
|
cc8d637715 | ||
|
|
eafc9ad78c | ||
|
|
c1719c8f1a | ||
|
|
d3fb567fdb | ||
|
|
8b1e5aa320 | ||
|
|
37e654faa0 | ||
|
|
7c73b5e3df | ||
|
|
6a9661b374 | ||
|
|
6429164852 | ||
|
|
fa68cbbe4e | ||
|
|
f323690049 | ||
|
|
02ffff01d5 | ||
|
|
2cdeb85cd6 | ||
|
|
6ea7c48ae9 | ||
|
|
65682aa60d | ||
|
|
bb1737daec | ||
|
|
fef494949f | ||
|
|
0a15d488c8 | ||
|
|
e69eb46911 | ||
|
|
8f8db3f542 | ||
|
|
8923704f3c | ||
|
|
ba2d122308 | ||
|
|
938bea9910 | ||
|
|
8719c7a2db | ||
|
|
16fa12f455 | ||
|
|
555b1c80e3 | ||
|
|
a12a5f387a | ||
|
|
c087e33b86 | ||
|
|
49c468f00a | ||
|
|
b7f79f0adc | ||
|
|
2756a106a9 | ||
|
|
efffa0def2 | ||
|
|
6beb1c135c | ||
|
|
24ab2e85e5 | ||
|
|
c6749560b5 | ||
|
|
a0160b8e47 | ||
|
|
2f9df8c8e2 | ||
|
|
0076f1251c | ||
|
|
fe911a7b7a | ||
|
|
bdd79a9984 | ||
|
|
513f43f465 | ||
|
|
f5116cddb4 | ||
|
|
bbe10004b4 | ||
|
|
e9252a9ee3 | ||
|
|
875b0739aa | ||
|
|
7f56115abc | ||
|
|
5a29f19faa | ||
|
|
ae45e158a3 | ||
|
|
81c44528ba | ||
|
|
9cfb6baaf2 | ||
|
|
1de902fe8d | ||
|
|
7ac141ba08 | ||
|
|
fd7838ef2c | ||
|
|
7ffb1de1c9 | ||
|
|
e4daa5a924 | ||
|
|
147022234e | ||
|
|
2054708c53 | ||
|
|
3fe3cf71ab | ||
|
|
7787885463 | ||
|
|
f320980006 | ||
|
|
7e0a3c114d | ||
|
|
a86d7d27c1 | ||
|
|
b00cf65732 | ||
|
|
69364b2183 | ||
|
|
6b3f175885 | ||
|
|
cb7aa561cf | ||
|
|
253869ad46 | ||
|
|
109da32164 | ||
|
|
b69c0ac746 | ||
|
|
4cc61c038e | ||
|
|
ae725feee9 | ||
|
|
49a57576dd | ||
|
|
b94b9ad2d8 | ||
|
|
b28e416d0c | ||
|
|
e13f4a7f29 | ||
|
|
f96cbea521 | ||
|
|
d4fb9be250 | ||
|
|
41cdb6bbaf | ||
|
|
f43b6f04dc | ||
|
|
791b785dea | ||
|
|
d7849c793c | ||
|
|
5568372bb9 | ||
|
|
cba26c92f5 | ||
|
|
47de5118f2 | ||
|
|
81f3acfa38 | ||
|
|
aac48fc308 | ||
|
|
5635d6c862 | ||
|
|
e9d0ac5110 | ||
|
|
7b36c8ab54 | ||
|
|
e04e5e0185 | ||
|
|
67d3b5fb82 | ||
|
|
c31a0e37bf | ||
|
|
ab01ce41ed | ||
|
|
1e6b8a0be0 | ||
|
|
dba0de88f3 | ||
|
|
0cdbb710d2 | ||
|
|
75f024cb19 | ||
|
|
32e4e0d835 | ||
|
|
7c85a511a2 | ||
|
|
6be92f92fb | ||
|
|
7277d00e1a | ||
|
|
29eb3dd384 | ||
|
|
501ec25f5a | ||
|
|
6ae0c0bd22 | ||
|
|
d9c1b104cd | ||
|
|
fbf4284866 | ||
|
|
06126544bd | ||
|
|
e9c93305b9 | ||
|
|
3197156aa1 | ||
|
|
f377292ffe | ||
|
|
96e4442181 | ||
|
|
afb032f8f9 | ||
|
|
d34f297b37 | ||
|
|
e0591500ce | ||
|
|
c43193a0c8 | ||
|
|
0896e7164a | ||
|
|
00567645d0 | ||
|
|
92b9d68863 | ||
|
|
0d709ea8db | ||
|
|
9a21cb9cc7 | ||
|
|
6424f45c19 | ||
|
|
8a961bfa21 | ||
|
|
cd86d57763 | ||
|
|
2753b468fd | ||
|
|
be1915d2d7 | ||
|
|
42ef46b74e | ||
|
|
79d0e74056 | ||
|
|
8de4524428 | ||
|
|
8f71f965b9 | ||
|
|
0e3641bba2 | ||
|
|
e3aa495e0b | ||
|
|
0edee37498 | ||
|
|
7968ed6d69 | ||
|
|
44edba6f75 | ||
|
|
0c7e95539f | ||
|
|
8938990c04 | ||
|
|
14b1b7c862 | ||
|
|
c8b90df6f1 | ||
|
|
d0bbfca831 | ||
|
|
fe5d037600 | ||
|
|
0dc7dfcb50 | ||
|
|
c9b61ef010 | ||
|
|
349039ff5d | ||
|
|
a72155a57f | ||
|
|
41f8663dcd | ||
|
|
84007cb1ec | ||
|
|
265c51b8aa | ||
|
|
4c5d041c21 | ||
|
|
ccef7bd1bc | ||
|
|
e2db152c6c | ||
|
|
5c490e999d | ||
|
|
a11e1527f0 | ||
|
|
6ac11a554a | ||
|
|
42dd8d1d9d | ||
|
|
2af6f4cdf6 | ||
|
|
60bdd3eccd | ||
|
|
c1b93179ff |
36
.github/ISSUE_TEMPLATE/add_new_k8s_version.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/add_new_k8s_version.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Add new K8s version
|
||||
about: 'Checklist for maintainers to add new K8s minor version'
|
||||
title: 'Add new K8s version vX.X'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Note: Please update the issue title to include the new Kubernetes version number. -->
|
||||
|
||||
# Adding a new Kubernetes Version
|
||||
|
||||
## `pinniped's ci branch`
|
||||
|
||||
- [ ] Update `dockerfile-builders` pipeline
|
||||
- [ ] Update `pull-requests` pipeline
|
||||
- [ ] Update `main` pipeline
|
||||
|
||||
## `pinniped`
|
||||
|
||||
- [ ] Bump all golang dependencies (especially the `k8s.io` dependencies to use the new minor version).
|
||||
- [ ] Be sure to verify that everything compiles and unit tests pass locally. This is probably a good starting point.
|
||||
```shell
|
||||
./hack/update-go-mod/update-go-mod.sh
|
||||
./hack/module.sh unit
|
||||
./hack/prepare-for-integration-tests.sh
|
||||
```
|
||||
- [ ] Log in to github as pinniped-ci-bot, then go to [this page](https://github.com/pinniped-ci-bot?tab=packages) and change the settings for the new `k8s-code-generator-1.*` image to be publicly visible
|
||||
- [ ] Add the new K8s version to `hack/lib/kube-versions.txt` and run code generation.
|
||||
|
||||
## General Tasks
|
||||
|
||||
- [ ] Consider dropping support for any older versions of Kubernetes
|
||||
- [ ] Create stories or chores to take advantage of features in the new Kubernetes version
|
||||
- [ ] Close this issue
|
||||
31
.github/ISSUE_TEMPLATE/release_checklist.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/release_checklist.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Release checklist
|
||||
about: Checklist for maintainers to prepare for an upcoming release
|
||||
title: 'Release checklist for vX.X.X'
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Note: Please update the issue title to include the planned release's version number. -->
|
||||
|
||||
# Release checklist
|
||||
|
||||
- [ ] Ensure that Pinniped's dependencies have been upgraded, to the extent desired by the team (refer to the diff output from the latest run of the [all-golang-deps-updated](https://ci.pinniped.dev/teams/main/pipelines/security-scan/jobs/all-golang-deps-updated/) CI job)
|
||||
- [ ] If you are updating golang in Pinniped, be sure to update golang in CI as well. Do a search-and-replace to update the version number everywhere in the pinniped `ci` branch.
|
||||
- [ ] If the Fosite library is being updated and the format of the content of the Supervisor's storage Secrets are changed, or if any change to our own code changes the format of the content of the Supervisor's session storage Secrets, then be sure to update the `accessTokenStorageVersion`, `authorizeCodeStorageVersion`, `oidcStorageVersion`, `pkceStorageVersion`, `refreshTokenStorageVersion`, variables in files such as `internal/fositestorage/accesstoken/accesstoken.go`. Failing tests should signal the need to update these values.
|
||||
- [ ] For go.mod direct dependencies that are v2 or above, such as `github.com/google/go-github/vXX`, check to see if there is a new major version available. Try using `hack/update-go-mod/update-majors.sh`.
|
||||
- [ ] Evaluate all `replace` directives in the `go.mod` file. Are they up to date versions? Can any `replace` directives be removed?
|
||||
- [ ] Ensure that Pinniped's codegen is up-to-date with the latest Kubernetes releases by making sure this [file](https://github.com/vmware-tanzu/pinniped/blob/main/hack/lib/kube-versions.txt) is updated compared to the latest releases listed [here for active branches](https://kubernetes.io/releases/) and [here for non-active branches](https://kubernetes.io/releases/patch-releases/#non-active-branch-history)
|
||||
- [ ] Ensure that the `k8s-code-generator` CI job definitions are up-to-date with the latest Go, K8s, and `controller-gen` versions
|
||||
- [ ] All relevant feature and docs PRs are merged
|
||||
- [ ] The [main pipeline](https://ci.pinniped.dev/teams/main/pipelines/main) is green, up to and including the `ready-to-release` job. Check that the expected git commit has passed the `ready-to-release` job.
|
||||
- [ ] Optional: a blog post for the release is written and submitted as a PR but not merged yet
|
||||
- [ ] All merged user stories are accepted (manually tested)
|
||||
- [ ] Only after all stories are accepted, manually trigger the `release` job to create a draft GitHub release
|
||||
- [ ] Manually edit the draft release notes on the [GitHub release](https://github.com/vmware-tanzu/pinniped/releases) to describe the contents of the release, using the format which was automatically added to the draft release
|
||||
- [ ] Publish (i.e. make public) the draft release
|
||||
- [ ] After making the release public, the jobs in the [main pipeline](https://ci.pinniped.dev/teams/main/pipelines/main) beyond the release job should auto-trigger, so check to make sure that they passed
|
||||
- [ ] Edit the blog post's date to make it match the actual release date, and merge the blog post PR to make it live on the website
|
||||
- [ ] Publicize the release via tweets, etc.
|
||||
- [ ] Close this issue
|
||||
122
.github/dependabot.yml
vendored
122
.github/dependabot.yml
vendored
@@ -9,12 +9,25 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
# Our own CI job is responsible for updating this go.mod file now.
|
||||
# - package-ecosystem: "gomod"
|
||||
# open-pull-requests-limit: 100
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "daily"
|
||||
# Use dependabot to automate major-only dependency bumps
|
||||
- package-ecosystem: "gomod"
|
||||
open-pull-requests-limit: 2 # Not sure why there would ever be more than 1, just would not want to hide anything
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
# group all major dependency bumps together so there's only one pull request
|
||||
groups:
|
||||
go-modules:
|
||||
patterns:
|
||||
- "*"
|
||||
update-types:
|
||||
- "major"
|
||||
ignore:
|
||||
# For all packages, ignore all minor and patch updates
|
||||
- dependency-name: "*"
|
||||
update-types:
|
||||
- "version-update:semver-minor"
|
||||
- "version-update:semver-patch"
|
||||
|
||||
# Our own CI job is responsible for updating this Docker file now.
|
||||
# - package-ecosystem: "docker"
|
||||
@@ -27,3 +40,100 @@ updates:
|
||||
# directory: "/hack" # this should keep the FIPS dockerfile updated per https://github.com/dependabot/feedback/issues/145#issuecomment-414738498
|
||||
# schedule:
|
||||
# interval: "daily"
|
||||
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/code-coverage-uploader/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/crane/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/deployment-yaml-formatter/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/eks-deployer/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/gh-cli/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/integration-test-runner/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/integration-test-runner-beta/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/k8s-app-deployer/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/k8s-code-generator/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/pool-trigger-resource/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/test-bitnami-ldap/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/test-cfssl/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/test-dex/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/test-forward-proxy/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/dockerfiles/test-kubectl/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/pipelines/shared-helpers/test-binaries-image/"
|
||||
open-pull-requests-limit: 100
|
||||
schedule:
|
||||
interval: "daily"
|
||||
target-branch: ci
|
||||
|
||||
189
.golangci.yaml
189
.golangci.yaml
@@ -1,28 +1,15 @@
|
||||
# https://github.com/golangci/golangci-lint#config-file
|
||||
run:
|
||||
deadline: 1m
|
||||
skip-dirs:
|
||||
- generated
|
||||
# https://golangci-lint.run/usage/configuration/
|
||||
|
||||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
# default linters
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
||||
|
||||
# additional linters for this project (we should disable these if they get annoying).
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
# - depguard
|
||||
- copyloopvar
|
||||
- dogsled
|
||||
- errcheck
|
||||
- exhaustive
|
||||
- exportloopref
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
@@ -30,49 +17,149 @@ linters:
|
||||
- gocyclo
|
||||
- godot
|
||||
- goheader
|
||||
- goimports
|
||||
- revive
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- govet
|
||||
- importas
|
||||
- ineffassign
|
||||
- intrange
|
||||
- makezero
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- exportloopref
|
||||
- spancheck
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unused
|
||||
- whitespace
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# exclude tests from some rules for things that are useful in a testing context.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
|
||||
linters-settings:
|
||||
funlen:
|
||||
lines: 150
|
||||
statements: 50
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
# YYYY or YYYY-YYYY
|
||||
YEARS: \d\d\d\d(-\d\d\d\d)?
|
||||
template: |-
|
||||
Copyright {{YEARS}} the Pinniped contributors. All Rights Reserved.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
goimports:
|
||||
local-prefixes: go.pinniped.dev
|
||||
revive:
|
||||
max-open-files: 2048
|
||||
settings:
|
||||
funlen:
|
||||
lines: 150
|
||||
statements: 50
|
||||
goheader:
|
||||
values:
|
||||
regexp:
|
||||
# YYYY or YYYY-YYYY
|
||||
YEARS: \d\d\d\d(-\d\d\d\d)?
|
||||
template: |-
|
||||
Copyright {{YEARS}} the Pinniped contributors. All Rights Reserved.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
importas:
|
||||
alias:
|
||||
- pkg: k8s.io/apimachinery/pkg/util/errors
|
||||
alias: utilerrors
|
||||
- pkg: k8s.io/apimachinery/pkg/api/errors
|
||||
alias: apierrors
|
||||
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
|
||||
alias: metav1
|
||||
- pkg: k8s.io/api/core/v1
|
||||
alias: corev1
|
||||
- pkg: github.com/coreos/go-oidc/v3/oidc
|
||||
alias: coreosoidc
|
||||
- pkg: github.com/ory/fosite/handler/oauth2
|
||||
alias: fositeoauth2
|
||||
- pkg: github.com/ory/fosite/token/jwt
|
||||
alias: fositejwt
|
||||
- pkg: github.com/go-jose/go-jose/v4/jwt
|
||||
alias: josejwt
|
||||
- pkg: github.com/go-jose/go-jose/v3
|
||||
alias: oldjosev3
|
||||
- pkg: go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1
|
||||
alias: authenticationv1alpha1
|
||||
- pkg: go.pinniped.dev/generated/latest/apis/supervisor/clientsecret/v1alpha1
|
||||
alias: clientsecretv1alpha1
|
||||
- pkg: go.pinniped.dev/generated/latest/apis/supervisor/config/v1alpha1
|
||||
alias: supervisorconfigv1alpha1
|
||||
- pkg: go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1
|
||||
alias: conciergeconfigv1alpha1
|
||||
- pkg: go.pinniped.dev/generated/latest/client/concierge/clientset/versioned
|
||||
alias: conciergeclientset
|
||||
- pkg: go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/scheme
|
||||
alias: conciergeclientsetscheme
|
||||
- pkg: go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake
|
||||
alias: conciergefake
|
||||
- pkg: go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned
|
||||
alias: supervisorclientset
|
||||
- pkg: go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/scheme
|
||||
alias: supervisorclientsetscheme
|
||||
- pkg: go.pinniped.dev/generated/latest/client/supervisor/clientset/versioned/fake
|
||||
alias: supervisorfake
|
||||
- pkg: go.pinniped.dev/generated/latest/apis/supervisor/idp/v1alpha1
|
||||
alias: idpv1alpha1
|
||||
- pkg: go.pinniped.dev/generated/latest/client/concierge/informers/externalversions
|
||||
alias: conciergeinformers
|
||||
- pkg: go.pinniped.dev/generated/latest/client/supervisor/informers/externalversions
|
||||
alias: supervisorinformers
|
||||
- pkg: go.pinniped.dev/internal/concierge/scheme
|
||||
alias: conciergescheme
|
||||
no-unaliased: true # All packages explicitly listed above must be aliased
|
||||
no-extra-aliases: false # Allow other aliases than the ones explicitly listed above
|
||||
revive:
|
||||
max-open-files: 2048
|
||||
rules:
|
||||
# Allow unused params that start with underscore. It can be nice to keep unused param names when implementing
|
||||
# an interface sometimes, to help readers understand why it is unused in that particular implementation.
|
||||
- name: unused-parameter
|
||||
arguments:
|
||||
- allowRegex: ^_
|
||||
spancheck:
|
||||
# https://golangci-lint.run/usage/linters/#spancheck
|
||||
checks:
|
||||
- end
|
||||
- record-error
|
||||
- set-status
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- name: unused-parameter
|
||||
arguments:
|
||||
# Allow unused params that start with underscore. It can be nice to keep unused param names when implementing
|
||||
# an interface sometimes, to help readers understand why it is unused in that particular implementation.
|
||||
- allowRegex: "^_"
|
||||
# exclude tests from some rules for things that are useful in a testing context.
|
||||
- linters:
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- revive
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- revive
|
||||
path: internal/testutil/
|
||||
paths:
|
||||
- generated
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
# Simplify code: gofmt with `-s` option.
|
||||
# Default: true
|
||||
simplify: false
|
||||
# Apply the rewrite rules to the source before reformatting.
|
||||
# https://pkg.go.dev/cmd/gofmt
|
||||
# Default: []
|
||||
rewrite-rules:
|
||||
- pattern: interface{}
|
||||
replacement: any
|
||||
- pattern: a[b:len(a)]
|
||||
replacement: a[b:]
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- go.pinniped.dev
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- generated
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
||||
@@ -184,6 +184,11 @@ the progress and results will appear on the Github page for that
|
||||
[pull request](https://github.com/vmware-tanzu/pinniped/pulls) as checks. Links
|
||||
will appear to view the details of each check.
|
||||
|
||||
## CI
|
||||
|
||||
Pinniped's CI configuration and code is in the [`ci`](https://github.com/vmware-tanzu/pinniped/tree/ci)
|
||||
branch of this repo. The CI results are visible to the public at https://ci.pinniped.dev.
|
||||
|
||||
## Documentation
|
||||
|
||||
Any pull request which adds a new feature or changes the behavior of any feature which was previously documented
|
||||
|
||||
15
Dockerfile
15
Dockerfile
@@ -1,13 +1,13 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
# Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
ARG BUILD_IMAGE=golang:1.22.3@sha256:b1e05e2c918f52c59d39ce7d5844f73b2f4511f7734add8bb98c9ecdd4443365
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:e9ac71e2b8e279a8372741b7a0293afda17650d926900233ec3a7b2b7c22a246
|
||||
ARG BUILD_IMAGE=golang:1.24.3@sha256:86b4cff66e04d41821a17cea30c1031ed53e2635e2be99ae0b4a7d69336b5063
|
||||
ARG BASE_IMAGE=gcr.io/distroless/static:nonroot@sha256:c0f429e16b13e583da7e5a6ec20dd656d325d88e6819cafe0adb0828976529dc
|
||||
|
||||
# Prepare to cross-compile by always running the build stage in the build platform, not the target platform.
|
||||
FROM --platform=$BUILDPLATFORM $BUILD_IMAGE as build-env
|
||||
FROM --platform=$BUILDPLATFORM $BUILD_IMAGE AS build-env
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
@@ -21,6 +21,9 @@ ENV KUBE_GIT_VERSION=$KUBE_GIT_VERSION
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
# If provided, must be a comma-separated list of Go build tags.
|
||||
ARG ADDITIONAL_BUILD_TAGS
|
||||
|
||||
# Build the statically linked (CGO_ENABLED=0) binary.
|
||||
# Mount source, build cache, and module cache for performance reasons.
|
||||
# See https://www.docker.com/blog/faster-multi-platform-builds-dockerfile-cross-compilation-guide/
|
||||
@@ -29,8 +32,8 @@ RUN \
|
||||
--mount=type=cache,target=/cache/gocache \
|
||||
--mount=type=cache,target=/cache/gomodcache \
|
||||
export GOCACHE=/cache/gocache GOMODCACHE=/cache/gomodcache CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH && \
|
||||
go build -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-concierge-kube-cert-agent ./cmd/pinniped-concierge-kube-cert-agent/... && \
|
||||
go build -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-server ./cmd/pinniped-server/... && \
|
||||
go build -tags $ADDITIONAL_BUILD_TAGS -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-concierge-kube-cert-agent ./cmd/pinniped-concierge-kube-cert-agent/... && \
|
||||
go build -tags $ADDITIONAL_BUILD_TAGS -v -trimpath -ldflags "$(hack/get-ldflags.sh) -w -s" -o /usr/local/bin/pinniped-server ./cmd/pinniped-server/... && \
|
||||
ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-concierge && \
|
||||
ln -s /usr/local/bin/pinniped-server /usr/local/bin/pinniped-supervisor && \
|
||||
ln -s /usr/local/bin/pinniped-server /usr/local/bin/local-user-authenticator
|
||||
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
| Maintainer | GitHub ID | Affiliation |
|
||||
|-----------------|-----------------------------------------------------------|------------------------------------------|
|
||||
| Ben Petersen | [benjaminapetersen](https://github.com/benjaminapetersen) | [VMware](https://www.github.com/vmware/) |
|
||||
| Ryan Richard | [cfryanr](https://github.com/cfryanr) | [VMware](https://www.github.com/vmware/) |
|
||||
| Joshua T. Casey | [joshuatcasey](https://github.com/joshuatcasey) | [VMware](https://www.github.com/vmware/) |
|
||||
|
||||
## Emeritus Maintainers
|
||||
|
||||
| Maintainer | GitHub ID |
|
||||
|-------------------|---------------------------------------------------------|
|
||||
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) |
|
||||
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) |
|
||||
| Margo Crawford | [margocrawf](https://github.com/margocrawf) |
|
||||
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) |
|
||||
| Mo Khan | [enj](https://github.com/enj) |
|
||||
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) |
|
||||
| Maintainer | GitHub ID |
|
||||
|-------------------|-----------------------------------------------------------|
|
||||
| Andrew Keesler | [ankeesler](https://github.com/ankeesler) |
|
||||
| Anjali Telang | [anjaltelang](https://github.com/anjaltelang) |
|
||||
| Ben Petersen | [benjaminapetersen](https://github.com/benjaminapetersen) |
|
||||
| Margo Crawford | [margocrawf](https://github.com/margocrawf) |
|
||||
| Matt Moyer | [mattmoyer](https://github.com/mattmoyer) |
|
||||
| Mo Khan | [enj](https://github.com/enj) |
|
||||
| Pablo Schuhmacher | [pabloschuhmacher](https://github.com/pabloschuhmacher) |
|
||||
|
||||
@@ -47,4 +47,4 @@ Please follow the procedure described in [SECURITY.md](SECURITY.md).
|
||||
|
||||
Pinniped is open source and licensed under Apache License Version 2.0. See [LICENSE](LICENSE).
|
||||
|
||||
Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
|
||||
17
ROADMAP.md
17
ROADMAP.md
@@ -5,8 +5,6 @@
|
||||
This document provides a high-level overview of the next big features the maintainers are planning to work on. This
|
||||
should serve as a reference point for Pinniped users and contributors to understand where the project is heading, and
|
||||
help determine if a contribution could be conflicting with a longer term plan.
|
||||
The [Pinniped project backlog](https://github.com/orgs/vmware-tanzu/projects/43/) is prioritized based on this roadmap,
|
||||
and it provides a more granular view of what the maintainers are working on a day-to-day basis.
|
||||
|
||||
### How to help
|
||||
|
||||
@@ -23,19 +21,6 @@ a [proposal](https://github.com/vmware-tanzu/pinniped/tree/main/proposals) in ou
|
||||
For smaller enhancements, you can open an issue to track that initiative or feature request.
|
||||
We work with and rely on community feedback to focus our efforts to improve Pinniped and maintain a healthy roadmap.
|
||||
|
||||
### Current Roadmap
|
||||
|
||||
The following table includes the current roadmap for Pinniped. Please take the timelines and dates as proposals and
|
||||
goals. Priorities and requirements change based on community feedback, roadblocks encountered, community contributions,
|
||||
Priorities and requirements change based on community feedback, roadblocks encountered, community contributions,
|
||||
etc. If you depend on a specific item, we encourage you to reach out for updated status information, or help us deliver
|
||||
that feature by [contributing](https://github.com/vmware-tanzu/pinniped/blob/main/CONTRIBUTING.md) to Pinniped.
|
||||
|
||||
Last Updated: Sept 2022
|
||||
|Theme|Description|Timeline|
|
||||
|--|--|--|
|
||||
|Improving Usability|Dynamic Oauth Client Support for integrating with UI/Dashboards |Sept/Oct 2022|
|
||||
|Improving Usability|Support for custom claim mappings in OIDCIdentityProvider |Q4 2022|
|
||||
|Improving Usability|Support for Multiple Identity Providers |Q4 2022|
|
||||
|Improving Security Posture|Support Audit logging of security events related to Authentication |Q4 2022|
|
||||
|Improving Security Posture|Session Management |2022/2023|
|
||||
|Improving Security Posture|Secrets Rotation and Management |2022/2023|
|
||||
|
||||
9
SCOPE.md
9
SCOPE.md
@@ -21,12 +21,3 @@ The following items are out of scope for the Pinniped project.
|
||||
- Standalone identity provider for general use.
|
||||
- Machine-to-machine (service) identity.
|
||||
- Running outside of Kubernetes.
|
||||
|
||||
## Roadmap
|
||||
|
||||
See our [open milestones][milestones] and the [`priority/backlog` label][backlog] for an idea about what's next on our roadmap.
|
||||
|
||||
For more details on proposing features and bugs, check out our [contributing](./CONTRIBUTING.md) doc.
|
||||
|
||||
[milestones]: https://github.com/vmware-tanzu/pinniped/milestones
|
||||
[backlog]: https://github.com/vmware-tanzu/pinniped/labels/priority%2Fbacklog
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -79,6 +79,7 @@ type JWTTokenClaims struct {
|
||||
// +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster
|
||||
// +kubebuilder:printcolumn:name="Issuer",type=string,JSONPath=`.spec.issuer`
|
||||
// +kubebuilder:printcolumn:name="Audience",type=string,JSONPath=`.spec.audience`
|
||||
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
// +kubebuilder:subresource:status
|
||||
type JWTAuthenticator struct {
|
||||
|
||||
@@ -1,11 +1,47 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// Configuration for configuring TLS on various authenticators.
|
||||
// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Secret;ConfigMap
|
||||
type CertificateAuthorityDataSourceKind string
|
||||
|
||||
const (
|
||||
// CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles.
|
||||
CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap")
|
||||
|
||||
// CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles.
|
||||
// Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque.
|
||||
CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret")
|
||||
)
|
||||
|
||||
// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification.
|
||||
type CertificateAuthorityDataSourceSpec struct {
|
||||
// Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
// Allowed values are "Secret" or "ConfigMap".
|
||||
// "ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
// "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
Kind CertificateAuthorityDataSourceKind `json:"kind"`
|
||||
// Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
// The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Name string `json:"name"`
|
||||
// Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
// The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
// certificate bundle.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// TLSSpec provides TLS configuration on various authenticators.
|
||||
type TLSSpec struct {
|
||||
// X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted.
|
||||
// +optional
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"`
|
||||
// Reference to a CA bundle in a secret or a configmap.
|
||||
// Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
// +optional
|
||||
CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -50,6 +50,7 @@ type WebhookAuthenticatorSpec struct {
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:resource:categories=pinniped;pinniped-authenticator;pinniped-authenticators,scope=Cluster
|
||||
// +kubebuilder:printcolumn:name="Endpoint",type=string,JSONPath=`.spec.endpoint`
|
||||
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
// +kubebuilder:subresource:status
|
||||
type WebhookAuthenticator struct {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -49,6 +49,7 @@ type CredentialIssuerSpec struct {
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
// Allowed values are "auto", "enabled", or "disabled".
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
@@ -65,6 +66,7 @@ const (
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
// Allowed values are "LoadBalancer", "ClusterIP", or "None".
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
@@ -159,24 +161,6 @@ type ImpersonationProxyServiceSpec struct {
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This field is deprecated and will be removed in a future version.
|
||||
// +optional
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:Pattern=`^https://|^http://`
|
||||
Server string `json:"server"`
|
||||
|
||||
// The K8s API server CA bundle.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package identity
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package identity
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package identity
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package validation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package login
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package login
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package login
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package clientsecret
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package clientsecret
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -55,6 +55,7 @@ type FederationDomainTransformsConstant struct {
|
||||
Name string `json:"name"`
|
||||
|
||||
// Type determines the type of the constant, and indicates which other field should be non-empty.
|
||||
// Allowed values are "string" or "stringList".
|
||||
// +kubebuilder:validation:Enum=string;stringList
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -70,6 +71,7 @@ type FederationDomainTransformsConstant struct {
|
||||
// FederationDomainTransformsExpression defines a transform expression.
|
||||
type FederationDomainTransformsExpression struct {
|
||||
// Type determines the type of the expression. It must be one of the supported types.
|
||||
// Allowed values are "policy/v1", "username/v1", or "groups/v1".
|
||||
// +kubebuilder:validation:Enum=policy/v1;username/v1;groups/v1
|
||||
Type string `json:"type"`
|
||||
|
||||
@@ -207,6 +209,7 @@ type FederationDomainSpec struct {
|
||||
// See
|
||||
// https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:XValidation:message="issuer must be an HTTPS URL",rule="isURL(self) && url(self).getScheme() == 'https'"
|
||||
Issuer string `json:"issuer"`
|
||||
|
||||
// TLS specifies a secret which will contain Transport Layer Security (TLS) configuration for the FederationDomain.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2022-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -36,6 +36,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&LDAPIdentityProviderList{},
|
||||
&ActiveDirectoryIdentityProvider{},
|
||||
&ActiveDirectoryIdentityProviderList{},
|
||||
&GitHubIdentityProvider{},
|
||||
&GitHubIdentityProviderList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
// Copyright 2024-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type GitHubIdentityProviderPhase string
|
||||
|
||||
const (
|
||||
// GitHubPhasePending is the default phase for newly-created GitHubIdentityProvider resources.
|
||||
GitHubPhasePending GitHubIdentityProviderPhase = "Pending"
|
||||
|
||||
// GitHubPhaseReady is the phase for an GitHubIdentityProvider resource in a healthy state.
|
||||
GitHubPhaseReady GitHubIdentityProviderPhase = "Ready"
|
||||
|
||||
// GitHubPhaseError is the phase for an GitHubIdentityProvider in an unhealthy state.
|
||||
GitHubPhaseError GitHubIdentityProviderPhase = "Error"
|
||||
)
|
||||
|
||||
type GitHubAllowedAuthOrganizationsPolicy string
|
||||
|
||||
const (
|
||||
// GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers means any GitHub user is allowed to log in using this identity
|
||||
// provider, regardless of their organization membership or lack thereof.
|
||||
GitHubAllowedAuthOrganizationsPolicyAllGitHubUsers GitHubAllowedAuthOrganizationsPolicy = "AllGitHubUsers"
|
||||
|
||||
// GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations means only those users with membership in
|
||||
// the listed GitHub organizations are allowed to log in.
|
||||
GitHubAllowedAuthOrganizationsPolicyOnlyUsersFromAllowedOrganizations GitHubAllowedAuthOrganizationsPolicy = "OnlyUsersFromAllowedOrganizations"
|
||||
)
|
||||
|
||||
// GitHubIdentityProviderStatus is the status of an GitHub identity provider.
|
||||
type GitHubIdentityProviderStatus struct {
|
||||
// Phase summarizes the overall status of the GitHubIdentityProvider.
|
||||
//
|
||||
// +kubebuilder:default=Pending
|
||||
// +kubebuilder:validation:Enum=Pending;Ready;Error
|
||||
Phase GitHubIdentityProviderPhase `json:"phase,omitempty"`
|
||||
|
||||
// Conditions represents the observations of an identity provider's current state.
|
||||
//
|
||||
// +patchMergeKey=type
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
|
||||
}
|
||||
|
||||
// GitHubAPIConfig allows configuration for GitHub Enterprise Server
|
||||
type GitHubAPIConfig struct {
|
||||
// Host is required only for GitHub Enterprise Server.
|
||||
// Defaults to using GitHub's public API ("github.com").
|
||||
// For convenience, specifying "github.com" is equivalent to specifying "api.github.com".
|
||||
// Do not specify a protocol or scheme since "https://" will always be used.
|
||||
// Port is optional. Do not specify a path, query, fragment, or userinfo.
|
||||
// Only specify domain name or IP address, subdomains (optional), and port (optional).
|
||||
// IPv4 and IPv6 are supported. If using an IPv6 address with a port, you must enclose the IPv6 address
|
||||
// in square brackets. Example: "[::1]:443".
|
||||
//
|
||||
// +kubebuilder:default="github.com"
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +optional
|
||||
Host *string `json:"host"`
|
||||
|
||||
// TLS configuration for GitHub Enterprise Server.
|
||||
// Note that this field should not be needed when using GitHub's public API ("github.com").
|
||||
// However, if you choose to specify this field when using GitHub's public API, you must
|
||||
// specify a CA bundle that will verify connections to "api.github.com".
|
||||
//
|
||||
// +optional
|
||||
TLS *TLSSpec `json:"tls,omitempty"`
|
||||
}
|
||||
|
||||
// GitHubUsernameAttribute allows the user to specify which attribute(s) from GitHub to use for the username to present
|
||||
// to Kubernetes. See the response schema for
|
||||
// [Get the authenticated user](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user).
|
||||
type GitHubUsernameAttribute string
|
||||
|
||||
const (
|
||||
// GitHubUsernameID specifies using the `id` attribute from the GitHub user for the username to present to Kubernetes.
|
||||
GitHubUsernameID GitHubUsernameAttribute = "id"
|
||||
|
||||
// GitHubUsernameLogin specifies using the `login` attribute from the GitHub user as the username to present to Kubernetes.
|
||||
GitHubUsernameLogin GitHubUsernameAttribute = "login"
|
||||
|
||||
// GitHubUsernameLoginAndID specifies combining the `login` and `id` attributes from the GitHub user as the
|
||||
// username to present to Kubernetes, separated by a colon. Example: "my-login:1234"
|
||||
GitHubUsernameLoginAndID GitHubUsernameAttribute = "login:id"
|
||||
)
|
||||
|
||||
// GitHubGroupNameAttribute allows the user to specify which attribute from GitHub to use for the group
|
||||
// names to present to Kubernetes. See the response schema for
|
||||
// [List teams for the authenticated user](https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user).
|
||||
type GitHubGroupNameAttribute string
|
||||
|
||||
const (
|
||||
// GitHubUseTeamNameForGroupName specifies using the GitHub team's `name` attribute as the group name to present to Kubernetes.
|
||||
GitHubUseTeamNameForGroupName GitHubGroupNameAttribute = "name"
|
||||
|
||||
// GitHubUseTeamSlugForGroupName specifies using the GitHub team's `slug` attribute as the group name to present to Kubernetes.
|
||||
GitHubUseTeamSlugForGroupName GitHubGroupNameAttribute = "slug"
|
||||
)
|
||||
|
||||
// GitHubClaims allows customization of the username and groups claims.
|
||||
type GitHubClaims struct {
|
||||
// Username configures which property of the GitHub user record shall determine the username in Kubernetes.
|
||||
//
|
||||
// Can be either "id", "login", or "login:id". Defaults to "login:id".
|
||||
//
|
||||
// GitHub's user login attributes can only contain alphanumeric characters and non-repeating hyphens,
|
||||
// and may not start or end with hyphens. GitHub users are allowed to change their login name,
|
||||
// although it is inconvenient. If a GitHub user changed their login name from "foo" to "bar",
|
||||
// then a second user might change their name from "baz" to "foo" in order to take the old
|
||||
// username of the first user. For this reason, it is not as safe to make authorization decisions
|
||||
// based only on the user's login attribute.
|
||||
//
|
||||
// If desired, an admin could configure identity transformation expressions on the Pinniped Supervisor's
|
||||
// FederationDomain to further customize how these usernames are presented to Kubernetes.
|
||||
//
|
||||
// Defaults to "login:id", which is the user login attribute, followed by a colon, followed by the unique and
|
||||
// unchanging integer ID number attribute. This blends human-readable login names with the unchanging ID value
|
||||
// from GitHub. Colons are not allowed in GitHub login attributes or ID numbers, so this is a reasonable
|
||||
// choice to concatenate the two values.
|
||||
//
|
||||
// See the response schema for
|
||||
// [Get the authenticated user](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user).
|
||||
//
|
||||
// +kubebuilder:default="login:id"
|
||||
// +kubebuilder:validation:Enum={"id","login","login:id"}
|
||||
// +optional
|
||||
Username *GitHubUsernameAttribute `json:"username"`
|
||||
|
||||
// Groups configures which property of the GitHub team record shall determine the group names in Kubernetes.
|
||||
//
|
||||
// Can be either "name" or "slug". Defaults to "slug".
|
||||
//
|
||||
// GitHub team names can contain upper and lower case characters, whitespace, and punctuation (e.g. "Kube admins!").
|
||||
//
|
||||
// GitHub team slugs are lower case alphanumeric characters and may contain dashes and underscores (e.g. "kube-admins").
|
||||
//
|
||||
// Group names as presented to Kubernetes will always be prefixed by the GitHub organization name followed by a
|
||||
// forward slash (e.g. "my-org/my-team"). GitHub organization login names can only contain alphanumeric characters
|
||||
// or single hyphens, so the first forward slash `/` will be the separator between the organization login name and
|
||||
// the team name or slug.
|
||||
//
|
||||
// If desired, an admin could configure identity transformation expressions on the Pinniped Supervisor's
|
||||
// FederationDomain to further customize how these group names are presented to Kubernetes.
|
||||
//
|
||||
// See the response schema for
|
||||
// [List teams for the authenticated user](https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user).
|
||||
//
|
||||
// +kubebuilder:default=slug
|
||||
// +kubebuilder:validation:Enum=name;slug
|
||||
// +optional
|
||||
Groups *GitHubGroupNameAttribute `json:"groups"`
|
||||
}
|
||||
|
||||
// GitHubClientSpec contains information about the GitHub client that this identity provider will use
|
||||
// for web-based login flows.
|
||||
type GitHubClientSpec struct {
|
||||
// SecretName contains the name of a namespace-local Secret object that provides the clientID and
|
||||
// clientSecret for an GitHub App or GitHub OAuth2 client.
|
||||
//
|
||||
// This secret must be of type "secrets.pinniped.dev/github-client" with keys "clientID" and "clientSecret".
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
SecretName string `json:"secretName"`
|
||||
}
|
||||
|
||||
type GitHubOrganizationsSpec struct {
|
||||
// Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers".
|
||||
// Defaults to "OnlyUsersFromAllowedOrganizations".
|
||||
//
|
||||
// Must be set to "AllGitHubUsers" if the allowed field is empty.
|
||||
//
|
||||
// This field only exists to ensure that Pinniped administrators are aware that an empty list of
|
||||
// allowedOrganizations means all GitHub users are allowed to log in.
|
||||
//
|
||||
// +kubebuilder:default=OnlyUsersFromAllowedOrganizations
|
||||
// +kubebuilder:validation:Enum=OnlyUsersFromAllowedOrganizations;AllGitHubUsers
|
||||
// +optional
|
||||
Policy *GitHubAllowedAuthOrganizationsPolicy `json:"policy"`
|
||||
|
||||
// Allowed, when specified, indicates that only users with membership in at least one of the listed
|
||||
// GitHub organizations may log in. In addition, the group membership presented to Kubernetes will only include
|
||||
// teams within the listed GitHub organizations. Additional login rules or group filtering can optionally be
|
||||
// provided as policy expression on any Pinniped Supervisor FederationDomain that includes this IDP.
|
||||
//
|
||||
// The configured GitHub App or GitHub OAuth App must be allowed to see membership in the listed organizations,
|
||||
// otherwise Pinniped will not be aware that the user belongs to the listed organization or any teams
|
||||
// within that organization.
|
||||
//
|
||||
// If no organizations are listed, you must set organizations: AllGitHubUsers.
|
||||
//
|
||||
// +kubebuilder:validation:MaxItems=64
|
||||
// +listType=set
|
||||
// +optional
|
||||
Allowed []string `json:"allowed,omitempty"`
|
||||
}
|
||||
|
||||
// GitHubAllowAuthenticationSpec allows customization of who can authenticate using this IDP and how.
|
||||
type GitHubAllowAuthenticationSpec struct {
|
||||
// Organizations allows customization of which organizations can authenticate using this IDP.
|
||||
// +kubebuilder:validation:XValidation:message="spec.allowAuthentication.organizations.policy must be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed has organizations listed",rule="!(has(self.allowed) && size(self.allowed) > 0 && self.policy == 'AllGitHubUsers')"
|
||||
// +kubebuilder:validation:XValidation:message="spec.allowAuthentication.organizations.policy must be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed is empty",rule="!((!has(self.allowed) || size(self.allowed) == 0) && self.policy == 'OnlyUsersFromAllowedOrganizations')"
|
||||
Organizations GitHubOrganizationsSpec `json:"organizations"`
|
||||
}
|
||||
|
||||
// GitHubIdentityProviderSpec is the spec for configuring an GitHub identity provider.
|
||||
type GitHubIdentityProviderSpec struct {
|
||||
// GitHubAPI allows configuration for GitHub Enterprise Server
|
||||
//
|
||||
// +kubebuilder:default={}
|
||||
GitHubAPI GitHubAPIConfig `json:"githubAPI,omitempty"`
|
||||
|
||||
// Claims allows customization of the username and groups claims.
|
||||
//
|
||||
// +kubebuilder:default={}
|
||||
Claims GitHubClaims `json:"claims,omitempty"`
|
||||
|
||||
// AllowAuthentication allows customization of who can authenticate using this IDP and how.
|
||||
AllowAuthentication GitHubAllowAuthenticationSpec `json:"allowAuthentication"`
|
||||
|
||||
// Client identifies the secret with credentials for a GitHub App or GitHub OAuth2 App (a GitHub client).
|
||||
Client GitHubClientSpec `json:"client"`
|
||||
}
|
||||
|
||||
// GitHubIdentityProvider describes the configuration of an upstream GitHub identity provider.
|
||||
// This upstream provider can be configured with either a GitHub App or a GitHub OAuth2 App.
|
||||
//
|
||||
// Right now, only web-based logins are supported, for both the pinniped-cli client and clients configured
|
||||
// as OIDCClients.
|
||||
//
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:resource:categories=pinniped;pinniped-idp;pinniped-idps
|
||||
// +kubebuilder:printcolumn:name="Host",type=string,JSONPath=`.spec.githubAPI.host`
|
||||
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.phase`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
// +kubebuilder:subresource:status
|
||||
type GitHubIdentityProvider struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec for configuring the identity provider.
|
||||
Spec GitHubIdentityProviderSpec `json:"spec"`
|
||||
|
||||
// Status of the identity provider.
|
||||
Status GitHubIdentityProviderStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GitHubIdentityProviderList lists GitHubIdentityProvider objects.
|
||||
//
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type GitHubIdentityProviderList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []GitHubIdentityProvider `json:"items"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,11 +1,47 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// Configuration for TLS parameters related to identity provider integration.
|
||||
// CertificateAuthorityDataSourceKind enumerates the sources for CA Bundles.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=Secret;ConfigMap
|
||||
type CertificateAuthorityDataSourceKind string
|
||||
|
||||
const (
|
||||
// CertificateAuthorityDataSourceKindConfigMap uses a Kubernetes configmap to source CA Bundles.
|
||||
CertificateAuthorityDataSourceKindConfigMap = CertificateAuthorityDataSourceKind("ConfigMap")
|
||||
|
||||
// CertificateAuthorityDataSourceKindSecret uses a Kubernetes secret to source CA Bundles.
|
||||
// Secrets used to source CA Bundles must be of type kubernetes.io/tls or Opaque.
|
||||
CertificateAuthorityDataSourceKindSecret = CertificateAuthorityDataSourceKind("Secret")
|
||||
)
|
||||
|
||||
// CertificateAuthorityDataSourceSpec provides a source for CA bundle used for client-side TLS verification.
|
||||
type CertificateAuthorityDataSourceSpec struct {
|
||||
// Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
// Allowed values are "Secret" or "ConfigMap".
|
||||
// "ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
// "Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
Kind CertificateAuthorityDataSourceKind `json:"kind"`
|
||||
// Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
// The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Name string `json:"name"`
|
||||
// Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
// The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
// certificate bundle.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// TLSSpec provides TLS configuration for identity provider integration.
|
||||
type TLSSpec struct {
|
||||
// X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted.
|
||||
// +optional
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"`
|
||||
// Reference to a CA bundle in a secret or a configmap.
|
||||
// Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
// +optional
|
||||
CertificateAuthorityDataSource *CertificateAuthorityDataSourceSpec `json:"certificateAuthorityDataSource,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -15,6 +15,7 @@ const (
|
||||
IDPTypeOIDC IDPType = "oidc"
|
||||
IDPTypeLDAP IDPType = "ldap"
|
||||
IDPTypeActiveDirectory IDPType = "activedirectory"
|
||||
IDPTypeGitHub IDPType = "github"
|
||||
|
||||
IDPFlowCLIPassword IDPFlow = "cli_password"
|
||||
IDPFlowBrowserAuthcode IDPFlow = "browser_authcode"
|
||||
@@ -54,7 +55,8 @@ type OIDCDiscoveryResponseIDPEndpoint struct {
|
||||
|
||||
// IDPDiscoveryResponse is the response of a FederationDomain's identity provider discovery endpoint.
|
||||
type IDPDiscoveryResponse struct {
|
||||
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
|
||||
PinnipedIDPs []PinnipedIDP `json:"pinniped_identity_providers"`
|
||||
PinnipedSupportedIDPTypes []PinnipedSupportedIDPType `json:"pinniped_supported_identity_provider_types"`
|
||||
}
|
||||
|
||||
// PinnipedIDP describes a single identity provider as included in the response of a FederationDomain's
|
||||
@@ -64,3 +66,8 @@ type PinnipedIDP struct {
|
||||
Type IDPType `json:"type"`
|
||||
Flows []IDPFlow `json:"flows,omitempty"`
|
||||
}
|
||||
|
||||
// PinnipedSupportedIDPType describes a single identity provider type.
|
||||
type PinnipedSupportedIDPType struct {
|
||||
Type IDPType `json:"type"`
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package oidc
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
@@ -90,12 +90,11 @@ func TestEntrypoint(t *testing.T) {
|
||||
}`,
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var logBuf bytes.Buffer
|
||||
testLog := log.New(&logBuf, "", 0)
|
||||
exited := "exiting via fatal"
|
||||
fail = func(format string, v ...interface{}) {
|
||||
fail = func(format string, v ...any) {
|
||||
testLog.Printf(format, v...)
|
||||
panic(exited)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
@@ -38,12 +38,11 @@ func TestEntrypoint(t *testing.T) {
|
||||
wantArgs: []string{"/path/to/valid-test-binary", "foo", "bar"},
|
||||
},
|
||||
} {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var logBuf bytes.Buffer
|
||||
testLog := log.New(&logBuf, "", 0)
|
||||
exited := "exiting via fatal"
|
||||
fail = func(err error, keysAndValues ...interface{}) {
|
||||
fail = func(err error, keysAndValues ...any) {
|
||||
testLog.Print(err)
|
||||
if len(keysAndValues) > 0 {
|
||||
testLog.Print(keysAndValues...)
|
||||
|
||||
46
cmd/pinniped/cmd/audit_id.go
Normal file
46
cmd/pinniped/cmd/audit_id.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"go.pinniped.dev/internal/httputil/roundtripper"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
)
|
||||
|
||||
type auditIDLoggerFunc func(path string, statusCode int, auditID string)
|
||||
|
||||
func logAuditID(path string, statusCode int, auditID string) {
|
||||
plog.Info("Received auditID for failed request",
|
||||
"path", path,
|
||||
"statusCode", statusCode,
|
||||
"auditID", auditID)
|
||||
}
|
||||
|
||||
func LogAuditIDTransportWrapper(rt http.RoundTripper) http.RoundTripper {
|
||||
return logAuditIDTransportWrapper(rt, logAuditID)
|
||||
}
|
||||
|
||||
func logAuditIDTransportWrapper(rt http.RoundTripper, auditIDLoggerFunc auditIDLoggerFunc) http.RoundTripper {
|
||||
return roundtripper.WrapFunc(rt, func(r *http.Request) (*http.Response, error) {
|
||||
response, responseErr := rt.RoundTrip(r)
|
||||
|
||||
if responseErr != nil ||
|
||||
response == nil ||
|
||||
response.Header.Get("audit-ID") == "" ||
|
||||
response.Request == nil ||
|
||||
response.Request.URL == nil {
|
||||
return response, responseErr
|
||||
}
|
||||
|
||||
// Use the request path from the response's request, in case the
|
||||
// original request was modified by any other roudtrippers in the chain.
|
||||
auditIDLoggerFunc(response.Request.URL.Path,
|
||||
response.StatusCode,
|
||||
response.Header.Get("audit-ID"))
|
||||
|
||||
return response, responseErr
|
||||
})
|
||||
}
|
||||
116
cmd/pinniped/cmd/audit_id_test.go
Normal file
116
cmd/pinniped/cmd/audit_id_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.pinniped.dev/internal/httputil/roundtripper"
|
||||
)
|
||||
|
||||
func TestLogAuditIDTransportWrapper(t *testing.T) {
|
||||
canonicalAuditIdHeaderName := "Audit-Id"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
response *http.Response
|
||||
responseErr error
|
||||
want func(t *testing.T, called func()) auditIDLoggerFunc
|
||||
wantCalled bool
|
||||
}{
|
||||
{
|
||||
name: "happy HTTP response - no error and no log",
|
||||
response: &http.Response{ // no headers
|
||||
StatusCode: http.StatusOK,
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "some-path-from-response-request",
|
||||
},
|
||||
},
|
||||
},
|
||||
responseErr: nil,
|
||||
want: func(t *testing.T, called func()) auditIDLoggerFunc {
|
||||
return func(_ string, _ int, _ string) {
|
||||
called()
|
||||
}
|
||||
},
|
||||
wantCalled: false, // make it obvious
|
||||
},
|
||||
{
|
||||
name: "nil HTTP response - no error and no log",
|
||||
response: nil,
|
||||
responseErr: nil,
|
||||
want: func(t *testing.T, called func()) auditIDLoggerFunc {
|
||||
return func(_ string, _ int, _ string) {
|
||||
called()
|
||||
}
|
||||
},
|
||||
wantCalled: false, // make it obvious
|
||||
},
|
||||
{
|
||||
name: "err HTTP response - no error and no log",
|
||||
response: nil,
|
||||
responseErr: errors.New("some error"),
|
||||
want: func(t *testing.T, called func()) auditIDLoggerFunc {
|
||||
return func(_ string, _ int, _ string) {
|
||||
called()
|
||||
}
|
||||
},
|
||||
wantCalled: false, // make it obvious
|
||||
},
|
||||
{
|
||||
name: "happy HTTP response with audit-ID - logs",
|
||||
response: &http.Response{
|
||||
Header: http.Header{
|
||||
canonicalAuditIdHeaderName: []string{"some-audit-id", "some-other-audit-id-that-will-never-be-seen"},
|
||||
},
|
||||
StatusCode: http.StatusBadGateway, // statusCode does not matter
|
||||
Request: &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "some-path-from-response-request",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: func(t *testing.T, called func()) auditIDLoggerFunc {
|
||||
return func(path string, statusCode int, auditID string) {
|
||||
called()
|
||||
require.Equal(t, "some-path-from-response-request", path)
|
||||
require.Equal(t, http.StatusBadGateway, statusCode)
|
||||
require.Equal(t, "some-audit-id", auditID)
|
||||
}
|
||||
},
|
||||
wantCalled: true, // make it obvious
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require.NotNil(t, test.want)
|
||||
|
||||
mockRequest := &http.Request{
|
||||
URL: &url.URL{
|
||||
Path: "should-never-use-this-path",
|
||||
},
|
||||
}
|
||||
var mockRt roundtripper.Func = func(r *http.Request) (*http.Response, error) {
|
||||
require.Equal(t, mockRequest, r)
|
||||
return test.response, test.responseErr
|
||||
}
|
||||
called := false
|
||||
subjectRt := logAuditIDTransportWrapper(mockRt, test.want(t, func() {
|
||||
called = true
|
||||
}))
|
||||
actualResponse, err := subjectRt.RoundTrip(mockRequest) //nolint:bodyclose // there is no Body.
|
||||
require.Equal(t, test.responseErr, err) // This roundtripper only returns mocked errors.
|
||||
require.Equal(t, test.response, actualResponse)
|
||||
require.Equal(t, test.wantCalled, called,
|
||||
"want logFunc to be called: %t, actually was called: %t", test.wantCalled, called)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
conciergeconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
)
|
||||
|
||||
// conciergeModeFlag represents the method by which we should connect to the Concierge on a cluster during login.
|
||||
@@ -62,12 +62,12 @@ func (f *conciergeModeFlag) Type() string {
|
||||
}
|
||||
|
||||
// MatchesFrontend returns true iff the flag matches the type of the provided frontend.
|
||||
func (f *conciergeModeFlag) MatchesFrontend(frontend *configv1alpha1.CredentialIssuerFrontend) bool {
|
||||
func (f *conciergeModeFlag) MatchesFrontend(frontend *conciergeconfigv1alpha1.CredentialIssuerFrontend) bool {
|
||||
switch *f {
|
||||
case modeImpersonationProxy:
|
||||
return frontend.Type == configv1alpha1.ImpersonationProxyFrontendType
|
||||
return frontend.Type == conciergeconfigv1alpha1.ImpersonationProxyFrontendType
|
||||
case modeTokenCredentialRequestAPI:
|
||||
return frontend.Type == configv1alpha1.TokenCredentialRequestAPIFrontendType
|
||||
return frontend.Type == conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType
|
||||
case modeUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
conciergeconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
)
|
||||
|
||||
@@ -24,14 +24,14 @@ func TestConciergeModeFlag(t *testing.T) {
|
||||
require.NoError(t, f.Set(""))
|
||||
require.Equal(t, modeUnknown, f)
|
||||
require.EqualError(t, f.Set("foo"), `invalid mode "foo", valid modes are TokenCredentialRequestAPI and ImpersonationProxy`)
|
||||
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, f.Set("TokenCredentialRequestAPI"))
|
||||
require.Equal(t, modeTokenCredentialRequestAPI, f)
|
||||
require.Equal(t, "TokenCredentialRequestAPI", f.String())
|
||||
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.False(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.False(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, f.Set("tokencredentialrequestapi"))
|
||||
require.Equal(t, modeTokenCredentialRequestAPI, f)
|
||||
@@ -40,8 +40,8 @@ func TestConciergeModeFlag(t *testing.T) {
|
||||
require.NoError(t, f.Set("ImpersonationProxy"))
|
||||
require.Equal(t, modeImpersonationProxy, f)
|
||||
require.Equal(t, "ImpersonationProxy", f.String())
|
||||
require.False(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&configv1alpha1.CredentialIssuerFrontend{Type: configv1alpha1.ImpersonationProxyFrontendType}))
|
||||
require.False(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType}))
|
||||
require.True(t, f.MatchesFrontend(&conciergeconfigv1alpha1.CredentialIssuerFrontend{Type: conciergeconfigv1alpha1.ImpersonationProxyFrontendType}))
|
||||
|
||||
require.NoError(t, f.Set("impersonationproxy"))
|
||||
require.Equal(t, modeImpersonationProxy, f)
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
"go.pinniped.dev/internal/groupsuffix"
|
||||
"go.pinniped.dev/internal/kubeclient"
|
||||
)
|
||||
|
||||
// getConciergeClientsetFunc is a function that can return a clientset for the Concierge API given a
|
||||
// getClientsetsFunc is a function that can return clients for the Concierge and Kubernetes APIs given a
|
||||
// clientConfig and the apiGroupSuffix with which the API is running.
|
||||
type getConciergeClientsetFunc func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error)
|
||||
type getClientsetsFunc func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, kubernetes.Interface, aggregatorclient.Interface, error)
|
||||
|
||||
// getRealConciergeClientset returns a real implementation of a conciergeclientset.Interface.
|
||||
func getRealConciergeClientset(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
|
||||
// getRealClientsets returns real implementations of the Concierge and Kubernetes client interfaces.
|
||||
func getRealClientsets(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, kubernetes.Interface, aggregatorclient.Interface, error) {
|
||||
restConfig, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
client, err := kubeclient.New(
|
||||
kubeclient.WithConfig(restConfig),
|
||||
kubeclient.WithMiddleware(groupsuffix.New(apiGroupSuffix)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
return client.PinnipedConcierge, nil
|
||||
return client.PinnipedConcierge, client.Kubernetes, client.Aggregation, nil
|
||||
}
|
||||
|
||||
// newClientConfig returns a clientcmd.ClientConfig given an optional kubeconfig path override and
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -19,14 +20,15 @@ import (
|
||||
coreosoidc "github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth" // Adds handlers for various dynamic auth plugins in client-go
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/utils/strings/slices"
|
||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
|
||||
conciergev1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
authenticationv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/authentication/v1alpha1"
|
||||
conciergeconfigv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
|
||||
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
@@ -36,15 +38,17 @@ import (
|
||||
)
|
||||
|
||||
type kubeconfigDeps struct {
|
||||
getenv func(key string) string
|
||||
getPathToSelf func() (string, error)
|
||||
getClientset getConciergeClientsetFunc
|
||||
getClientsets getClientsetsFunc
|
||||
log plog.MinLogger
|
||||
}
|
||||
|
||||
func kubeconfigRealDeps() kubeconfigDeps {
|
||||
return kubeconfigDeps{
|
||||
getenv: os.Getenv,
|
||||
getPathToSelf: os.Executable,
|
||||
getClientset: getRealConciergeClientset,
|
||||
getClientsets: getRealClientsets,
|
||||
log: plog.New(),
|
||||
}
|
||||
}
|
||||
@@ -143,9 +147,20 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
|
||||
f.BoolVar(&flags.oidc.debugSessionCache, "oidc-debug-session-cache", false, "Print debug logs related to the OpenID Connect session cache")
|
||||
f.StringVar(&flags.oidc.requestAudience, "oidc-request-audience", "", "Request a token with an alternate audience using RFC8693 token exchange")
|
||||
f.StringVar(&flags.oidc.upstreamIDPName, "upstream-identity-provider-name", "", "The name of the upstream identity provider used during login with a Supervisor")
|
||||
f.StringVar(&flags.oidc.upstreamIDPType, "upstream-identity-provider-type", "", fmt.Sprintf("The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s')", idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory))
|
||||
f.StringVar(
|
||||
&flags.oidc.upstreamIDPType,
|
||||
"upstream-identity-provider-type",
|
||||
"",
|
||||
fmt.Sprintf(
|
||||
"The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s', '%s')",
|
||||
idpdiscoveryv1alpha1.IDPTypeOIDC,
|
||||
idpdiscoveryv1alpha1.IDPTypeLDAP,
|
||||
idpdiscoveryv1alpha1.IDPTypeActiveDirectory,
|
||||
idpdiscoveryv1alpha1.IDPTypeGitHub,
|
||||
),
|
||||
)
|
||||
f.StringVar(&flags.oidc.upstreamIDPFlow, "upstream-identity-provider-flow", "", fmt.Sprintf("The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. '%s', '%s')", idpdiscoveryv1alpha1.IDPFlowCLIPassword, idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode))
|
||||
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
|
||||
f.StringVar(&flags.kubeconfigPath, "kubeconfig", deps.getenv("KUBECONFIG"), "Path to kubeconfig file")
|
||||
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
|
||||
f.BoolVar(&flags.skipValidate, "skip-validation", false, "Skip final validation of the kubeconfig (default: false)")
|
||||
f.DurationVar(&flags.timeout, "timeout", 10*time.Minute, "Timeout for autodiscovery and validation")
|
||||
@@ -163,7 +178,7 @@ func kubeconfigCommand(deps kubeconfigDeps) *cobra.Command {
|
||||
|
||||
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
cmd.RunE = func(cmd *cobra.Command, _args []string) error {
|
||||
if flags.outputPath != "" {
|
||||
out, err := os.Create(flags.outputPath)
|
||||
if err != nil {
|
||||
@@ -182,7 +197,7 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
ctx, cancel := context.WithTimeout(ctx, flags.timeout)
|
||||
defer cancel()
|
||||
|
||||
// the log statements in this file assume that Info logs are unconditionally printed so we set the global level to info
|
||||
// the log statements in this file assume that Info logs are unconditionally printed, so we set the global level to info
|
||||
if err := plog.ValidateAndSetLogLevelAndFormatGlobally(ctx, plog.LogSpec{Level: plog.LevelInfo, Format: plog.FormatCLI}); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +217,7 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
return fmt.Errorf("could not load --kubeconfig/--kubeconfig-context: %w", err)
|
||||
}
|
||||
cluster := currentKubeConfig.Clusters[currentKubeconfigNames.ClusterName]
|
||||
clientset, err := deps.getClientset(clientConfig, flags.concierge.apiGroupSuffix)
|
||||
conciergeClient, kubeClient, aggregatorClient, err := deps.getClientsets(clientConfig, flags.concierge.apiGroupSuffix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not configure Kubernetes client: %w", err)
|
||||
}
|
||||
@@ -215,13 +230,15 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
}
|
||||
|
||||
if !flags.concierge.disabled {
|
||||
credentialIssuer, err := waitForCredentialIssuer(ctx, clientset, flags, deps)
|
||||
// Look up the Concierge's CredentialIssuer, and optionally wait for it to have no pending strategies showing in its status.
|
||||
credentialIssuer, err := waitForCredentialIssuer(ctx, conciergeClient, flags, deps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Decide which Concierge authenticator should be used in the resulting kubeconfig.
|
||||
authenticator, err := lookupAuthenticator(
|
||||
clientset,
|
||||
conciergeClient,
|
||||
flags.concierge.authenticatorType,
|
||||
flags.concierge.authenticatorName,
|
||||
deps.log,
|
||||
@@ -229,10 +246,15 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Discover from the CredentialIssuer how the resulting kubeconfig should be configured to talk to this Concierge.
|
||||
if err := discoverConciergeParams(credentialIssuer, &flags, cluster, deps.log); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := discoverAuthenticatorParams(authenticator, &flags, deps.log); err != nil {
|
||||
|
||||
// Discover how the resulting kubeconfig should interact with the selected authenticator.
|
||||
// For a JWTAuthenticator, this includes discovering how to talk to the OIDC issuer configured in its spec fields.
|
||||
if err := discoverAuthenticatorParams(ctx, authenticator, &flags, kubeClient, aggregatorClient, deps.log); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -242,6 +264,7 @@ func runGetKubeconfig(ctx context.Context, out io.Writer, deps kubeconfigDeps, f
|
||||
}
|
||||
|
||||
if len(flags.oidc.issuer) > 0 {
|
||||
// The OIDC provider may or may not be a Pinniped Supervisor. Find out.
|
||||
err = pinnipedSupervisorDiscovery(ctx, &flags, deps.log)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -303,7 +326,7 @@ func newExecConfig(deps kubeconfigDeps, flags getKubeconfigParams) (*clientcmdap
|
||||
if flags.staticToken != "" && flags.staticTokenEnvName != "" {
|
||||
return nil, fmt.Errorf("only one of --static-token and --static-token-env can be specified")
|
||||
}
|
||||
execConfig.Args = append([]string{"login", "static"}, execConfig.Args...)
|
||||
execConfig.Args = slices.Concat([]string{"login", "static"}, execConfig.Args)
|
||||
if flags.staticToken != "" {
|
||||
execConfig.Args = append(execConfig.Args, "--token="+flags.staticToken)
|
||||
}
|
||||
@@ -314,7 +337,7 @@ func newExecConfig(deps kubeconfigDeps, flags getKubeconfigParams) (*clientcmdap
|
||||
}
|
||||
|
||||
// Otherwise continue to parse the OIDC-related flags and output a config that runs `pinniped login oidc`.
|
||||
execConfig.Args = append([]string{"login", "oidc"}, execConfig.Args...)
|
||||
execConfig.Args = slices.Concat([]string{"login", "oidc"}, execConfig.Args)
|
||||
if flags.oidc.issuer == "" {
|
||||
return nil, fmt.Errorf("could not autodiscover --oidc-issuer and none was provided")
|
||||
}
|
||||
@@ -380,7 +403,7 @@ func getCurrentContext(currentKubeConfig clientcmdapi.Config, flags getKubeconfi
|
||||
return &kubeconfigNames{ContextName: contextName, UserName: ctx.AuthInfo, ClusterName: ctx.Cluster}, nil
|
||||
}
|
||||
|
||||
func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.Interface, flags getKubeconfigParams, deps kubeconfigDeps) (*configv1alpha1.CredentialIssuer, error) {
|
||||
func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.Interface, flags getKubeconfigParams, deps kubeconfigDeps) (*conciergeconfigv1alpha1.CredentialIssuer, error) {
|
||||
credentialIssuer, err := lookupCredentialIssuer(clientset, flags.concierge.credentialIssuer, deps.log)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -393,10 +416,7 @@ func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.I
|
||||
deadline, _ := ctx.Deadline()
|
||||
attempts := 1
|
||||
|
||||
for {
|
||||
if !hasPendingStrategy(credentialIssuer) {
|
||||
break
|
||||
}
|
||||
for hasPendingStrategy(credentialIssuer) {
|
||||
logStrategies(credentialIssuer, deps.log)
|
||||
deps.log.Info("waiting for CredentialIssuer pending strategies to finish",
|
||||
"attempts", attempts,
|
||||
@@ -416,7 +436,7 @@ func waitForCredentialIssuer(ctx context.Context, clientset conciergeclientset.I
|
||||
return credentialIssuer, nil
|
||||
}
|
||||
|
||||
func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, log plog.MinLogger) error {
|
||||
func discoverConciergeParams(credentialIssuer *conciergeconfigv1alpha1.CredentialIssuer, flags *getKubeconfigParams, v1Cluster *clientcmdapi.Cluster, log plog.MinLogger) error {
|
||||
// Autodiscover the --concierge-mode.
|
||||
frontend, err := getConciergeFrontend(credentialIssuer, flags.concierge.mode)
|
||||
if err != nil {
|
||||
@@ -427,10 +447,10 @@ func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer,
|
||||
// Auto-set --concierge-mode if it wasn't explicitly set.
|
||||
if flags.concierge.mode == modeUnknown {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
case conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
log.Info("discovered Concierge operating in TokenCredentialRequest API mode")
|
||||
flags.concierge.mode = modeTokenCredentialRequestAPI
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
case conciergeconfigv1alpha1.ImpersonationProxyFrontendType:
|
||||
log.Info("discovered Concierge operating in impersonation proxy mode")
|
||||
flags.concierge.mode = modeImpersonationProxy
|
||||
}
|
||||
@@ -439,9 +459,9 @@ func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer,
|
||||
// Auto-set --concierge-endpoint if it wasn't explicitly set.
|
||||
if flags.concierge.endpoint == "" {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
case conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
flags.concierge.endpoint = v1Cluster.Server
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
case conciergeconfigv1alpha1.ImpersonationProxyFrontendType:
|
||||
flags.concierge.endpoint = frontend.ImpersonationProxyInfo.Endpoint
|
||||
}
|
||||
log.Info("discovered Concierge endpoint", "endpoint", flags.concierge.endpoint)
|
||||
@@ -450,9 +470,9 @@ func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer,
|
||||
// Auto-set --concierge-ca-bundle if it wasn't explicitly set..
|
||||
if len(flags.concierge.caBundle) == 0 {
|
||||
switch frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
case conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType:
|
||||
flags.concierge.caBundle = v1Cluster.CertificateAuthorityData
|
||||
case configv1alpha1.ImpersonationProxyFrontendType:
|
||||
case conciergeconfigv1alpha1.ImpersonationProxyFrontendType:
|
||||
data, err := base64.StdEncoding.DecodeString(frontend.ImpersonationProxyInfo.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("autodiscovered Concierge CA bundle is invalid: %w", err)
|
||||
@@ -464,7 +484,7 @@ func discoverConciergeParams(credentialIssuer *configv1alpha1.CredentialIssuer,
|
||||
return nil
|
||||
}
|
||||
|
||||
func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log plog.MinLogger) {
|
||||
func logStrategies(credentialIssuer *conciergeconfigv1alpha1.CredentialIssuer, log plog.MinLogger) {
|
||||
for _, strategy := range credentialIssuer.Status.Strategies {
|
||||
log.Info("found CredentialIssuer strategy",
|
||||
"type", strategy.Type,
|
||||
@@ -475,9 +495,16 @@ func logStrategies(credentialIssuer *configv1alpha1.CredentialIssuer, log plog.M
|
||||
}
|
||||
}
|
||||
|
||||
func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconfigParams, log plog.MinLogger) error {
|
||||
func discoverAuthenticatorParams(
|
||||
ctx context.Context,
|
||||
authenticator metav1.Object,
|
||||
flags *getKubeconfigParams,
|
||||
kubeClient kubernetes.Interface,
|
||||
aggregatorClient aggregatorclient.Interface,
|
||||
log plog.MinLogger,
|
||||
) error {
|
||||
switch auth := authenticator.(type) {
|
||||
case *conciergev1alpha1.WebhookAuthenticator:
|
||||
case *authenticationv1alpha1.WebhookAuthenticator:
|
||||
// If the --concierge-authenticator-type/--concierge-authenticator-name flags were not set explicitly, set
|
||||
// them to point at the discovered WebhookAuthenticator.
|
||||
if flags.concierge.authenticatorType == "" && flags.concierge.authenticatorName == "" {
|
||||
@@ -485,7 +512,7 @@ func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconf
|
||||
flags.concierge.authenticatorType = "webhook"
|
||||
flags.concierge.authenticatorName = auth.Name
|
||||
}
|
||||
case *conciergev1alpha1.JWTAuthenticator:
|
||||
case *authenticationv1alpha1.JWTAuthenticator:
|
||||
// If the --concierge-authenticator-type/--concierge-authenticator-name flags were not set explicitly, set
|
||||
// them to point at the discovered JWTAuthenticator.
|
||||
if flags.concierge.authenticatorType == "" && flags.concierge.authenticatorName == "" {
|
||||
@@ -507,46 +534,146 @@ func discoverAuthenticatorParams(authenticator metav1.Object, flags *getKubeconf
|
||||
}
|
||||
|
||||
// If the --oidc-ca-bundle flags was not set explicitly, default it to the
|
||||
// spec.tls.certificateAuthorityData field of the JWTAuthenticator.
|
||||
if len(flags.oidc.caBundle) == 0 && auth.Spec.TLS != nil && auth.Spec.TLS.CertificateAuthorityData != "" {
|
||||
decoded, err := base64.StdEncoding.DecodeString(auth.Spec.TLS.CertificateAuthorityData)
|
||||
// spec.tls.certificateAuthorityData field of the JWTAuthenticator, if that field is set, or else
|
||||
// try to discover it from the spec.tls.certificateAuthorityDataSource, if that field is set.
|
||||
if len(flags.oidc.caBundle) == 0 && auth.Spec.TLS != nil {
|
||||
err := discoverOIDCCABundle(ctx, auth, flags, kubeClient, aggregatorClient, log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator %s has invalid spec.tls.certificateAuthorityData: %w", auth.Name, err)
|
||||
return err
|
||||
}
|
||||
log.Info("discovered OIDC CA bundle", "roots", countCACerts(decoded))
|
||||
flags.oidc.caBundle = decoded
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getConciergeFrontend(credentialIssuer *configv1alpha1.CredentialIssuer, mode conciergeModeFlag) (*configv1alpha1.CredentialIssuerFrontend, error) {
|
||||
func discoverOIDCCABundle(
|
||||
ctx context.Context,
|
||||
jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator,
|
||||
flags *getKubeconfigParams,
|
||||
kubeClient kubernetes.Interface,
|
||||
aggregatorClient aggregatorclient.Interface,
|
||||
log plog.MinLogger,
|
||||
) error {
|
||||
if jwtAuthenticator.Spec.TLS.CertificateAuthorityData != "" {
|
||||
decodedCABundleData, err := base64.StdEncoding.DecodeString(jwtAuthenticator.Spec.TLS.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator %s has invalid spec.tls.certificateAuthorityData: %w", jwtAuthenticator.Name, err)
|
||||
}
|
||||
log.Info("discovered OIDC CA bundle", "roots", countCACerts(decodedCABundleData))
|
||||
flags.oidc.caBundle = decodedCABundleData
|
||||
} else if jwtAuthenticator.Spec.TLS.CertificateAuthorityDataSource != nil {
|
||||
caBundleData, err := discoverOIDCCABundleFromCertificateAuthorityDataSource(
|
||||
ctx, jwtAuthenticator, flags.concierge.apiGroupSuffix, kubeClient, aggregatorClient, log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
flags.oidc.caBundle = caBundleData
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func discoverOIDCCABundleFromCertificateAuthorityDataSource(
|
||||
ctx context.Context,
|
||||
jwtAuthenticator *authenticationv1alpha1.JWTAuthenticator,
|
||||
apiGroupSuffix string,
|
||||
kubeClient kubernetes.Interface,
|
||||
aggregatorClient aggregatorclient.Interface,
|
||||
log plog.MinLogger,
|
||||
) ([]byte, error) {
|
||||
conciergeNamespace, err := discoverConciergeNamespace(ctx, apiGroupSuffix, aggregatorClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but encountered error discovering namespace of Concierge for JWTAuthenticator %s: %w", jwtAuthenticator.Name, err)
|
||||
}
|
||||
log.Info("discovered Concierge namespace for API group suffix", "apiGroupSuffix", apiGroupSuffix)
|
||||
|
||||
var caBundleData []byte
|
||||
var keyExisted bool
|
||||
caSource := jwtAuthenticator.Spec.TLS.CertificateAuthorityDataSource
|
||||
|
||||
// Note that the Kind, Name, and Key fields must all be non-empty, and Kind must be Secret or ConfigMap, due to CRD validations.
|
||||
switch caSource.Kind {
|
||||
case authenticationv1alpha1.CertificateAuthorityDataSourceKindConfigMap:
|
||||
caBundleConfigMap, err := kubeClient.CoreV1().ConfigMaps(conciergeNamespace).Get(ctx, caSource.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but encountered error getting %s %s/%s specified by JWTAuthenticator %s spec.tls.certificateAuthorityDataSource: %w",
|
||||
caSource.Kind, conciergeNamespace, caSource.Name, jwtAuthenticator.Name, err)
|
||||
}
|
||||
var caBundleDataStr string
|
||||
caBundleDataStr, keyExisted = caBundleConfigMap.Data[caSource.Key]
|
||||
caBundleData = []byte(caBundleDataStr)
|
||||
case authenticationv1alpha1.CertificateAuthorityDataSourceKindSecret:
|
||||
caBundleSecret, err := kubeClient.CoreV1().Secrets(conciergeNamespace).Get(ctx, caSource.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but encountered error getting %s %s/%s specified by JWTAuthenticator %s spec.tls.certificateAuthorityDataSource: %w",
|
||||
caSource.Kind, conciergeNamespace, caSource.Name, jwtAuthenticator.Name, err)
|
||||
}
|
||||
caBundleData, keyExisted = caBundleSecret.Data[caSource.Key]
|
||||
default:
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but JWTAuthenticator %s spec.tls.certificateAuthorityDataSource.Kind value %q is not supported by this CLI version",
|
||||
jwtAuthenticator.Name, caSource.Kind)
|
||||
}
|
||||
|
||||
if !keyExisted {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but key %q specified by JWTAuthenticator %s spec.tls.certificateAuthorityDataSource.key does not exist in %s %s/%s",
|
||||
caSource.Key, jwtAuthenticator.Name, caSource.Kind, conciergeNamespace, caSource.Name)
|
||||
}
|
||||
|
||||
if len(caBundleData) == 0 {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but key %q specified by JWTAuthenticator %s spec.tls.certificateAuthorityDataSource.key exists but has empty value in %s %s/%s",
|
||||
caSource.Key, jwtAuthenticator.Name, caSource.Kind, conciergeNamespace, caSource.Name)
|
||||
}
|
||||
|
||||
numCACerts := countCACerts(caBundleData)
|
||||
if numCACerts == 0 {
|
||||
return nil, fmt.Errorf("tried to autodiscover --oidc-ca-bundle, but value at key %q specified by JWTAuthenticator %s spec.tls.certificateAuthorityDataSource.key does not contain any CA certificates in %s %s/%s",
|
||||
caSource.Key, jwtAuthenticator.Name, caSource.Kind, conciergeNamespace, caSource.Name)
|
||||
}
|
||||
|
||||
log.Info("discovered OIDC CA bundle from JWTAuthenticator spec.tls.certificateAuthorityDataSource", "roots", numCACerts)
|
||||
return caBundleData, nil
|
||||
}
|
||||
|
||||
func discoverConciergeNamespace(ctx context.Context, apiGroupSuffix string, aggregatorClient aggregatorclient.Interface) (string, error) {
|
||||
// Let's look for the APIService for the API group of the Concierge's TokenCredentialRequest aggregated API.
|
||||
apiGroup := "login.concierge." + apiGroupSuffix
|
||||
|
||||
// List all APIServices.
|
||||
apiServiceList, err := aggregatorClient.ApiregistrationV1().APIServices().List(ctx, metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error listing APIServices: %w", err)
|
||||
}
|
||||
|
||||
// Find one with the expected API group name.
|
||||
for _, apiService := range apiServiceList.Items {
|
||||
if apiService.Spec.Group == apiGroup {
|
||||
if apiService.Spec.Service.Namespace != "" {
|
||||
// We are assuming that all API versions (e.g. v1alpha1) of this API group are backed by service(s)
|
||||
// in the same namespace, which is the namespace of the Concierge hosting this API suffix.
|
||||
return apiService.Spec.Service.Namespace, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find any APIService for the expected API group name which contained a namespace reference in its spec.
|
||||
return "", fmt.Errorf("could not find APIService with non-empty spec.service.namespace for API group %s", apiGroup)
|
||||
}
|
||||
|
||||
func getConciergeFrontend(credentialIssuer *conciergeconfigv1alpha1.CredentialIssuer, mode conciergeModeFlag) (*conciergeconfigv1alpha1.CredentialIssuerFrontend, error) {
|
||||
for _, strategy := range credentialIssuer.Status.Strategies {
|
||||
// Skip unhealthy strategies.
|
||||
if strategy.Status != configv1alpha1.SuccessStrategyStatus {
|
||||
if strategy.Status != conciergeconfigv1alpha1.SuccessStrategyStatus {
|
||||
continue
|
||||
}
|
||||
|
||||
// Backfill the .status.strategies[].frontend field from .status.kubeConfigInfo for backwards compatibility.
|
||||
if strategy.Type == configv1alpha1.KubeClusterSigningCertificateStrategyType && strategy.Frontend == nil && credentialIssuer.Status.KubeConfigInfo != nil {
|
||||
strategy = *strategy.DeepCopy()
|
||||
strategy.Frontend = &configv1alpha1.CredentialIssuerFrontend{
|
||||
Type: configv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
TokenCredentialRequestAPIInfo: &configv1alpha1.TokenCredentialRequestAPIInfo{
|
||||
Server: credentialIssuer.Status.KubeConfigInfo.Server,
|
||||
CertificateAuthorityData: credentialIssuer.Status.KubeConfigInfo.CertificateAuthorityData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If the strategy frontend is still nil, skip.
|
||||
// If the strategy frontend is nil, skip.
|
||||
if strategy.Frontend == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip any unknown frontend types.
|
||||
switch strategy.Frontend.Type {
|
||||
case configv1alpha1.TokenCredentialRequestAPIFrontendType, configv1alpha1.ImpersonationProxyFrontendType:
|
||||
case conciergeconfigv1alpha1.TokenCredentialRequestAPIFrontendType,
|
||||
conciergeconfigv1alpha1.ImpersonationProxyFrontendType:
|
||||
default:
|
||||
continue
|
||||
}
|
||||
@@ -574,7 +701,7 @@ func newExecKubeconfig(cluster *clientcmdapi.Cluster, execConfig *clientcmdapi.E
|
||||
}
|
||||
}
|
||||
|
||||
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string, log plog.MinLogger) (*configv1alpha1.CredentialIssuer, error) {
|
||||
func lookupCredentialIssuer(clientset conciergeclientset.Interface, name string, log plog.MinLogger) (*conciergeconfigv1alpha1.CredentialIssuer, error) {
|
||||
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancelFunc()
|
||||
|
||||
@@ -732,13 +859,12 @@ func validateKubeconfig(ctx context.Context, flags getKubeconfigParams, kubeconf
|
||||
func countCACerts(pemData []byte) int {
|
||||
pool := x509.NewCertPool()
|
||||
pool.AppendCertsFromPEM(pemData)
|
||||
//nolint:staticcheck // since we're not using .Subjects() to access the system pool
|
||||
return len(pool.Subjects())
|
||||
return len(pool.Subjects()) //nolint:staticcheck // there's no other clear way to mimic this legacy behavior
|
||||
}
|
||||
|
||||
func hasPendingStrategy(credentialIssuer *configv1alpha1.CredentialIssuer) bool {
|
||||
func hasPendingStrategy(credentialIssuer *conciergeconfigv1alpha1.CredentialIssuer) bool {
|
||||
for _, strategy := range credentialIssuer.Status.Strategies {
|
||||
if strategy.Reason == configv1alpha1.PendingStrategyReason {
|
||||
if strategy.Reason == conciergeconfigv1alpha1.PendingStrategyReason {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -783,12 +909,12 @@ func pinnipedSupervisorDiscovery(ctx context.Context, flags *getKubeconfigParams
|
||||
return err
|
||||
}
|
||||
if !supervisorSupportsBothUsernameAndGroupsScopes {
|
||||
flags.oidc.scopes = slices.Filter(nil, flags.oidc.scopes, func(scope string) bool {
|
||||
flags.oidc.scopes = slices.DeleteFunc(flags.oidc.scopes, func(scope string) bool {
|
||||
if scope == oidcapi.ScopeUsername || scope == oidcapi.ScopeGroups {
|
||||
log.Info("removed scope from --oidc-scopes list because it is not supported by this Supervisor", "scope", scope)
|
||||
return false // Remove username and groups scopes if there were present in the flags.
|
||||
return true // Remove username and groups scopes if there were present in the flags.
|
||||
}
|
||||
return true // Keep any other scopes in the flag list.
|
||||
return false // Keep any other scopes in the flag list.
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -59,9 +58,10 @@ func init() {
|
||||
}
|
||||
|
||||
type oidcLoginCommandDeps struct {
|
||||
lookupEnv func(string) (string, bool)
|
||||
login func(string, string, ...oidcclient.Option) (*oidctypes.Token, error)
|
||||
exchangeToken func(context.Context, *conciergeclient.Client, string) (*clientauthv1beta1.ExecCredential, error)
|
||||
lookupEnv func(string) (string, bool)
|
||||
login func(string, string, ...oidcclient.Option) (*oidctypes.Token, error)
|
||||
exchangeToken func(context.Context, *conciergeclient.Client, string) (*clientauthv1beta1.ExecCredential, error)
|
||||
optionsFactory OIDCClientOptions
|
||||
}
|
||||
|
||||
func oidcLoginCommandRealDeps() oidcLoginCommandDeps {
|
||||
@@ -71,6 +71,7 @@ func oidcLoginCommandRealDeps() oidcLoginCommandDeps {
|
||||
exchangeToken: func(ctx context.Context, client *conciergeclient.Client, token string) (*clientauthv1beta1.ExecCredential, error) {
|
||||
return client.ExchangeToken(ctx, token)
|
||||
},
|
||||
optionsFactory: &clientOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,14 +141,23 @@ func oidcLoginCommand(deps oidcLoginCommandDeps) *cobra.Command {
|
||||
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
|
||||
cmd.Flags().StringVar(&flags.credentialCachePath, "credential-cache", filepath.Join(mustGetConfigDir(), "credentials.yaml"), "Path to cluster-specific credentials cache (\"\" disables the cache)")
|
||||
cmd.Flags().StringVar(&flags.upstreamIdentityProviderName, "upstream-identity-provider-name", "", "The name of the upstream identity provider used during login with a Supervisor")
|
||||
cmd.Flags().StringVar(&flags.upstreamIdentityProviderType, "upstream-identity-provider-type", idpdiscoveryv1alpha1.IDPTypeOIDC.String(), fmt.Sprintf("The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s')", idpdiscoveryv1alpha1.IDPTypeOIDC, idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory))
|
||||
cmd.Flags().StringVar(&flags.upstreamIdentityProviderType,
|
||||
"upstream-identity-provider-type",
|
||||
idpdiscoveryv1alpha1.IDPTypeOIDC.String(),
|
||||
fmt.Sprintf(
|
||||
"The type of the upstream identity provider used during login with a Supervisor (e.g. '%s', '%s', '%s', '%s')",
|
||||
idpdiscoveryv1alpha1.IDPTypeOIDC,
|
||||
idpdiscoveryv1alpha1.IDPTypeLDAP,
|
||||
idpdiscoveryv1alpha1.IDPTypeActiveDirectory,
|
||||
idpdiscoveryv1alpha1.IDPTypeGitHub,
|
||||
))
|
||||
cmd.Flags().StringVar(&flags.upstreamIdentityProviderFlow, "upstream-identity-provider-flow", "", fmt.Sprintf("The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. '%s', '%s')", idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode, idpdiscoveryv1alpha1.IDPFlowCLIPassword))
|
||||
|
||||
// --skip-listen is mainly needed for testing. We'll leave it hidden until we have a non-testing use case.
|
||||
mustMarkHidden(cmd, "skip-listen")
|
||||
mustMarkHidden(cmd, "debug-session-cache")
|
||||
mustMarkRequired(cmd, "issuer")
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runOIDCLogin(cmd, deps, flags) }
|
||||
cmd.RunE = func(cmd *cobra.Command, _args []string) error { return runOIDCLogin(cmd, deps, flags) }
|
||||
|
||||
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
|
||||
mustMarkHidden(cmd, "concierge-namespace")
|
||||
@@ -166,48 +176,45 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
|
||||
// If the hidden --debug-session-cache option is passed, log all the errors from the session cache.
|
||||
if flags.debugSessionCache {
|
||||
logger := plog.WithName("session")
|
||||
sessionOptions = append(sessionOptions, filesession.WithErrorReporter(func(err error) {
|
||||
logger.Error("error during session cache operation", err)
|
||||
pLogger.Error("error during session cache operation", err)
|
||||
}))
|
||||
}
|
||||
sessionCache := filesession.New(flags.sessionCachePath, sessionOptions...)
|
||||
|
||||
// Initialize the login handler.
|
||||
opts := []oidcclient.Option{
|
||||
oidcclient.WithContext(cmd.Context()),
|
||||
oidcclient.WithLogger(plog.Logr()), //nolint:staticcheck // old code with lots of log statements
|
||||
oidcclient.WithScopes(flags.scopes),
|
||||
oidcclient.WithSessionCache(sessionCache),
|
||||
deps.optionsFactory.WithContext(cmd.Context()),
|
||||
deps.optionsFactory.WithLoginLogger(pLogger),
|
||||
deps.optionsFactory.WithScopes(flags.scopes),
|
||||
deps.optionsFactory.WithSessionCache(sessionCache),
|
||||
}
|
||||
|
||||
skipPrintLoginURL, _ := deps.lookupEnv(skipPrintLoginURLEnvVarName)
|
||||
if skipPrintLoginURL == envVarTruthyValue {
|
||||
opts = append(opts, oidcclient.WithSkipPrintLoginURL())
|
||||
opts = append(opts, deps.optionsFactory.WithSkipPrintLoginURL())
|
||||
}
|
||||
|
||||
if flags.listenPort != 0 {
|
||||
opts = append(opts, oidcclient.WithListenPort(flags.listenPort))
|
||||
opts = append(opts, deps.optionsFactory.WithListenPort(flags.listenPort))
|
||||
}
|
||||
|
||||
if flags.requestAudience != "" {
|
||||
opts = append(opts, oidcclient.WithRequestAudience(flags.requestAudience))
|
||||
opts = append(opts, deps.optionsFactory.WithRequestAudience(flags.requestAudience))
|
||||
}
|
||||
|
||||
if flags.upstreamIdentityProviderName != "" {
|
||||
opts = append(opts, oidcclient.WithUpstreamIdentityProvider(
|
||||
opts = append(opts, deps.optionsFactory.WithUpstreamIdentityProvider(
|
||||
flags.upstreamIdentityProviderName, flags.upstreamIdentityProviderType))
|
||||
}
|
||||
|
||||
flowOpts, err := flowOptions(
|
||||
idpdiscoveryv1alpha1.IDPType(flags.upstreamIdentityProviderType),
|
||||
idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow),
|
||||
deps,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
requestedFlow, flowSource := idpdiscoveryv1alpha1.IDPFlow(flags.upstreamIdentityProviderFlow), "--upstream-identity-provider-flow"
|
||||
if flowOverride, hasFlowOverride := deps.lookupEnv(upstreamIdentityProviderFlowEnvVarName); hasFlowOverride {
|
||||
requestedFlow, flowSource = idpdiscoveryv1alpha1.IDPFlow(flowOverride), upstreamIdentityProviderFlowEnvVarName
|
||||
}
|
||||
if requestedFlow != "" {
|
||||
opts = append(opts, deps.optionsFactory.WithLoginFlow(requestedFlow, flowSource))
|
||||
}
|
||||
opts = append(opts, flowOpts...)
|
||||
|
||||
var concierge *conciergeclient.Client
|
||||
if flags.conciergeEnabled {
|
||||
@@ -217,6 +224,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
conciergeclient.WithBase64CABundle(flags.conciergeCABundle),
|
||||
conciergeclient.WithAuthenticator(flags.conciergeAuthenticatorType, flags.conciergeAuthenticatorName),
|
||||
conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix),
|
||||
conciergeclient.WithTransportWrapper(LogAuditIDTransportWrapper),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid Concierge parameters: %w", err)
|
||||
@@ -225,12 +233,12 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
|
||||
// --skip-browser skips opening the browser.
|
||||
if flags.skipBrowser {
|
||||
opts = append(opts, oidcclient.WithSkipBrowserOpen())
|
||||
opts = append(opts, deps.optionsFactory.WithSkipBrowserOpen())
|
||||
}
|
||||
|
||||
// --skip-listen skips starting the localhost callback listener.
|
||||
if flags.skipListen {
|
||||
opts = append(opts, oidcclient.WithSkipListen())
|
||||
opts = append(opts, deps.optionsFactory.WithSkipListen())
|
||||
}
|
||||
|
||||
if len(flags.caBundlePaths) > 0 || len(flags.caBundleData) > 0 {
|
||||
@@ -238,7 +246,7 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts = append(opts, oidcclient.WithClient(client))
|
||||
opts = append(opts, deps.optionsFactory.WithClient(client))
|
||||
}
|
||||
// Look up cached credentials based on a hash of all the CLI arguments and the cluster info.
|
||||
cacheKey := struct {
|
||||
@@ -288,60 +296,6 @@ func runOIDCLogin(cmd *cobra.Command, deps oidcLoginCommandDeps, flags oidcLogin
|
||||
return json.NewEncoder(cmd.OutOrStdout()).Encode(cred)
|
||||
}
|
||||
|
||||
func flowOptions(
|
||||
requestedIDPType idpdiscoveryv1alpha1.IDPType,
|
||||
requestedFlow idpdiscoveryv1alpha1.IDPFlow,
|
||||
deps oidcLoginCommandDeps,
|
||||
) ([]oidcclient.Option, error) {
|
||||
useCLIFlow := []oidcclient.Option{oidcclient.WithCLISendingCredentials()}
|
||||
|
||||
// If the env var is set to override the --upstream-identity-provider-type flag, then override it.
|
||||
flowOverride, hasFlowOverride := deps.lookupEnv(upstreamIdentityProviderFlowEnvVarName)
|
||||
flowSource := "--upstream-identity-provider-flow"
|
||||
if hasFlowOverride {
|
||||
requestedFlow = idpdiscoveryv1alpha1.IDPFlow(flowOverride)
|
||||
flowSource = upstreamIdentityProviderFlowEnvVarName
|
||||
}
|
||||
|
||||
switch requestedIDPType {
|
||||
case idpdiscoveryv1alpha1.IDPTypeOIDC:
|
||||
switch requestedFlow {
|
||||
case idpdiscoveryv1alpha1.IDPFlowCLIPassword:
|
||||
return useCLIFlow, nil
|
||||
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode, "":
|
||||
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
|
||||
flowSource, requestedIDPType, requestedFlow,
|
||||
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String(), idpdiscoveryv1alpha1.IDPFlowCLIPassword.String()}, ", "))
|
||||
}
|
||||
case idpdiscoveryv1alpha1.IDPTypeLDAP, idpdiscoveryv1alpha1.IDPTypeActiveDirectory:
|
||||
switch requestedFlow {
|
||||
case idpdiscoveryv1alpha1.IDPFlowCLIPassword, "":
|
||||
return useCLIFlow, nil
|
||||
case idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode:
|
||||
return nil, nil // browser authcode flow is the default Option, so don't need to return an Option here
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
"%s value not recognized for identity provider type %q: %s (supported values: %s)",
|
||||
flowSource, requestedIDPType, requestedFlow,
|
||||
strings.Join([]string{idpdiscoveryv1alpha1.IDPFlowCLIPassword.String(), idpdiscoveryv1alpha1.IDPFlowBrowserAuthcode.String()}, ", "))
|
||||
}
|
||||
default:
|
||||
// Surprisingly cobra does not support this kind of flag validation. See https://github.com/spf13/pflag/issues/236
|
||||
return nil, fmt.Errorf(
|
||||
"--upstream-identity-provider-type value not recognized: %s (supported values: %s)",
|
||||
requestedIDPType,
|
||||
strings.Join([]string{
|
||||
idpdiscoveryv1alpha1.IDPTypeOIDC.String(),
|
||||
idpdiscoveryv1alpha1.IDPTypeLDAP.String(),
|
||||
idpdiscoveryv1alpha1.IDPTypeActiveDirectory.String(),
|
||||
}, ", "),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func makeClient(caBundlePaths []string, caBundleData []string) (*http.Client, error) {
|
||||
pool := x509.NewCertPool()
|
||||
for _, p := range caBundlePaths {
|
||||
@@ -385,8 +339,7 @@ func SetLogLevel(ctx context.Context, lookupEnv func(string) (string, bool)) (pl
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
logger := plog.New().WithName("pinniped-login")
|
||||
return logger, nil
|
||||
return plog.New(), nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -14,12 +14,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
clientauthv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||
clocktesting "k8s.io/utils/clock/testing"
|
||||
|
||||
idpdiscoveryv1alpha1 "go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
|
||||
oidcapi "go.pinniped.dev/generated/latest/apis/supervisor/oidc"
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
"go.pinniped.dev/internal/here"
|
||||
"go.pinniped.dev/internal/mocks/mockoidcclientoptions"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
"go.pinniped.dev/pkg/conciergeclient"
|
||||
@@ -42,6 +46,13 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
nowStr := now.Local().Format(time.RFC1123)
|
||||
|
||||
defaultWantedOptions := func(f *mockoidcclientoptions.MockOIDCClientOptions) {
|
||||
f.EXPECT().WithContext(gomock.Any())
|
||||
f.EXPECT().WithLoginLogger(gomock.Any())
|
||||
f.EXPECT().WithScopes([]string{oidcapi.ScopeOfflineAccess, oidcapi.ScopeOpenID, oidcapi.ScopeRequestAudience, oidcapi.ScopeUsername, oidcapi.ScopeGroups})
|
||||
f.EXPECT().WithSessionCache(gomock.Any())
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
@@ -51,6 +62,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
wantError bool
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
wantOptions func(f *mockoidcclientoptions.MockOIDCClientOptions)
|
||||
wantOptionsCount int
|
||||
wantLogs []string
|
||||
}{
|
||||
@@ -91,7 +103,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
--skip-browser Skip opening the browser (just print the URL)
|
||||
--upstream-identity-provider-flow string The type of client flow to use with the upstream identity provider during login with a Supervisor (e.g. 'browser_authcode', 'cli_password')
|
||||
--upstream-identity-provider-name string The name of the upstream identity provider used during login with a Supervisor
|
||||
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory') (default "oidc")
|
||||
--upstream-identity-provider-type string The type of the upstream identity provider used during login with a Supervisor (e.g. 'oidc', 'ldap', 'activedirectory', 'github') (default "oidc")
|
||||
`),
|
||||
},
|
||||
{
|
||||
@@ -109,7 +121,8 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--issuer", "test-issuer",
|
||||
"--enable-concierge",
|
||||
},
|
||||
wantError: true,
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: invalid Concierge parameters: endpoint must not be empty
|
||||
`),
|
||||
@@ -121,7 +134,8 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--issuer", "test-issuer",
|
||||
"--ca-bundle", "./does/not/exist",
|
||||
},
|
||||
wantError: true,
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: could not read --ca-bundle: open ./does/not/exist: no such file or directory
|
||||
`),
|
||||
@@ -133,7 +147,8 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--issuer", "test-issuer",
|
||||
"--ca-bundle-data", "invalid-base64",
|
||||
},
|
||||
wantError: true,
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: could not read --ca-bundle-data: illegal base64 data at input byte 7
|
||||
`),
|
||||
@@ -148,34 +163,12 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--concierge-authenticator-name", "test-authenticator",
|
||||
"--concierge-endpoint", "https://127.0.0.1:1234/",
|
||||
},
|
||||
wantError: true,
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: invalid Concierge parameters: invalid API group suffix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "invalid upstream type is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--upstream-identity-provider-type", "invalid",
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "invalid upstream type when flow override env var is used is still an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--upstream-identity-provider-type", "invalid",
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: --upstream-identity-provider-type value not recognized: invalid (supported values: oidc, ldap, activedirectory)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with default flow is allowed",
|
||||
args: []string{
|
||||
@@ -184,6 +177,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
@@ -195,269 +189,45 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_SKIP_PRINT_LOGIN_URL": "true"},
|
||||
env: map[string]string{"PINNIPED_SKIP_PRINT_LOGIN_URL": "true"},
|
||||
wantOptions: func(f *mockoidcclientoptions.MockOIDCClientOptions) {
|
||||
defaultWantedOptions(f)
|
||||
f.EXPECT().WithSkipPrintLoginURL()
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with CLI flow is allowed",
|
||||
name: "--upstream-identity-provider-flow adds an option",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with browser flow is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with CLI flow in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with with browser flow in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with unsupported flow is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "foobar",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: --upstream-identity-provider-flow value not recognized for identity provider type "oidc": foobar (supported values: browser_authcode, cli_password)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "oidc upstream type with unsupported flow in flow override env var is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "oidc",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "oidc": foo (supported values: browser_authcode, cli_password)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with default flow is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
wantOptions: func(f *mockoidcclientoptions.MockOIDCClientOptions) {
|
||||
defaultWantedOptions(f)
|
||||
f.EXPECT().WithLoginFlow(idpdiscoveryv1alpha1.IDPFlowCLIPassword, "--upstream-identity-provider-flow")
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "activedirectory upstream type with default flow is allowed",
|
||||
name: "PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW adds an option that overrides --upstream-identity-provider-flow",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "ignored-value-from-param",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "actual-value-from-env"},
|
||||
wantOptions: func(f *mockoidcclientoptions.MockOIDCClientOptions) {
|
||||
defaultWantedOptions(f)
|
||||
f.EXPECT().WithLoginFlow(idpdiscoveryv1alpha1.IDPFlow("actual-value-from-env"), "PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW")
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with CLI flow is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with browser_authcode flow is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with CLI flow in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with browser_authcode flow in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with unsupported flow is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "foo",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: --upstream-identity-provider-flow value not recognized for identity provider type "ldap": foo (supported values: cli_password, browser_authcode)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "ldap upstream type with unsupported flow in flow override env var is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "ldap": foo (supported values: cli_password, browser_authcode)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with CLI flow is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with browser_authcode is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with CLI flow in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "cli_password"},
|
||||
wantOptionsCount: 5,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with browser_authcode in flow override env var is allowed",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "cli_password",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "browser_authcode"},
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with unsupported flow is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "foo",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: --upstream-identity-provider-flow value not recognized for identity provider type "activedirectory": foo (supported values: cli_password, browser_authcode)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "active directory upstream type with unsupported flow in flow override env var is an error",
|
||||
args: []string{
|
||||
"--issuer", "test-issuer",
|
||||
"--client-id", "test-client-id",
|
||||
"--upstream-identity-provider-type", "activedirectory",
|
||||
"--upstream-identity-provider-flow", "browser_authcode",
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW": "foo"},
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
Error: PINNIPED_UPSTREAM_IDENTITY_PROVIDER_FLOW value not recognized for identity provider type "activedirectory": foo (supported values: cli_password, browser_authcode)
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "login error",
|
||||
args: []string{
|
||||
@@ -466,6 +236,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
loginErr: fmt.Errorf("some login error"),
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantOptionsCount: 4,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@@ -484,6 +255,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
conciergeErr: fmt.Errorf("some concierge error"),
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantOptionsCount: 4,
|
||||
wantError: true,
|
||||
wantStderr: here.Doc(`
|
||||
@@ -498,11 +270,12 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--credential-cache", "", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
},
|
||||
env: map[string]string{"PINNIPED_DEBUG": "true"},
|
||||
wantOptions: defaultWantedOptions,
|
||||
wantOptionsCount: 4,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"expirationTimestamp":"3020-10-12T13:14:15Z","token":"test-id-token"}}` + "\n",
|
||||
wantLogs: []string{
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:260 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:280 No concierge configured, skipping token credential exchange`,
|
||||
nowStr + ` cmd/login_oidc.go:268 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||
nowStr + ` cmd/login_oidc.go:288 No concierge configured, skipping token credential exchange`,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -526,24 +299,45 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
"--credential-cache", t.TempDir() + "/credentials.yaml", // must specify --credential-cache or else the cache file on disk causes test pollution
|
||||
"--upstream-identity-provider-name", "some-upstream-name",
|
||||
"--upstream-identity-provider-type", "ldap",
|
||||
"--upstream-identity-provider-flow", "some-flow-type",
|
||||
},
|
||||
env: map[string]string{"PINNIPED_DEBUG": "true", "PINNIPED_SKIP_PRINT_LOGIN_URL": "true"},
|
||||
wantOptions: func(f *mockoidcclientoptions.MockOIDCClientOptions) {
|
||||
f.EXPECT().WithContext(gomock.Any())
|
||||
f.EXPECT().WithLoginLogger(gomock.Any())
|
||||
f.EXPECT().WithScopes([]string{oidcapi.ScopeOfflineAccess, oidcapi.ScopeOpenID, oidcapi.ScopeRequestAudience, oidcapi.ScopeUsername, oidcapi.ScopeGroups})
|
||||
f.EXPECT().WithSessionCache(gomock.Any())
|
||||
f.EXPECT().WithListenPort(uint16(1234))
|
||||
f.EXPECT().WithSkipBrowserOpen()
|
||||
f.EXPECT().WithSkipListen()
|
||||
f.EXPECT().WithSkipPrintLoginURL()
|
||||
f.EXPECT().WithClient(gomock.Any())
|
||||
f.EXPECT().WithRequestAudience("cluster-1234")
|
||||
f.EXPECT().WithLoginFlow(idpdiscoveryv1alpha1.IDPFlow("some-flow-type"), "--upstream-identity-provider-flow")
|
||||
f.EXPECT().WithUpstreamIdentityProvider("some-upstream-name", "ldap")
|
||||
},
|
||||
env: map[string]string{"PINNIPED_DEBUG": "true", "PINNIPED_SKIP_PRINT_LOGIN_URL": "true"},
|
||||
wantOptionsCount: 12,
|
||||
wantStdout: `{"kind":"ExecCredential","apiVersion":"client.authentication.k8s.io/v1beta1","spec":{"interactive":false},"status":{"token":"exchanged-token"}}` + "\n",
|
||||
wantLogs: []string{
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:260 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:270 Exchanging token for cluster credential {"endpoint": "https://127.0.0.1:1234/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:278 Successfully exchanged token for cluster credential.`,
|
||||
nowStr + ` pinniped-login cmd/login_oidc.go:285 caching cluster credential for future use.`,
|
||||
nowStr + ` cmd/login_oidc.go:268 Performing OIDC login {"issuer": "test-issuer", "client id": "test-client-id"}`,
|
||||
nowStr + ` cmd/login_oidc.go:278 Exchanging token for cluster credential {"endpoint": "https://127.0.0.1:1234/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
|
||||
nowStr + ` cmd/login_oidc.go:286 Successfully exchanged token for cluster credential.`,
|
||||
nowStr + ` cmd/login_oidc.go:293 caching cluster credential for future use.`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
ctx := plog.AddZapOverridesToContext(context.Background(), t, &buf, nil, clocktesting.NewFakeClock(now))
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
t.Cleanup(ctrl.Finish)
|
||||
optionsFactory := mockoidcclientoptions.NewMockOIDCClientOptions(ctrl)
|
||||
if tt.wantOptions != nil {
|
||||
tt.wantOptions(optionsFactory)
|
||||
}
|
||||
|
||||
var gotOptions []oidcclient.Option
|
||||
cmd := oidcLoginCommand(oidcLoginCommandDeps{
|
||||
lookupEnv: func(s string) (string, bool) {
|
||||
@@ -579,6 +373,7 @@ func TestLoginOIDCCommand(t *testing.T) {
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
optionsFactory: optionsFactory,
|
||||
})
|
||||
require.NotNil(t, cmd)
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ func staticLoginCommand(deps staticLoginDeps) *cobra.Command {
|
||||
cmd.Flags().StringVar(&flags.conciergeAPIGroupSuffix, "concierge-api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
|
||||
cmd.Flags().StringVar(&flags.credentialCachePath, "credential-cache", filepath.Join(mustGetConfigDir(), "credentials.yaml"), "Path to cluster-specific credentials cache (\"\" disables the cache)")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error { return runStaticLogin(cmd, deps, flags) }
|
||||
cmd.RunE = func(cmd *cobra.Command, _args []string) error { return runStaticLogin(cmd, deps, flags) }
|
||||
|
||||
mustMarkDeprecated(cmd, "concierge-namespace", "not needed anymore")
|
||||
mustMarkHidden(cmd, "concierge-namespace")
|
||||
@@ -113,6 +113,7 @@ func runStaticLogin(cmd *cobra.Command, deps staticLoginDeps, flags staticLoginP
|
||||
conciergeclient.WithBase64CABundle(flags.conciergeCABundle),
|
||||
conciergeclient.WithAuthenticator(flags.conciergeAuthenticatorType, flags.conciergeAuthenticatorName),
|
||||
conciergeclient.WithAPIGroupSuffix(flags.conciergeAPIGroupSuffix),
|
||||
conciergeclient.WithTransportWrapper(LogAuditIDTransportWrapper),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid Concierge parameters: %w", err)
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestLoginStaticCommand(t *testing.T) {
|
||||
Error: could not complete Concierge credential exchange: some concierge error
|
||||
`),
|
||||
wantLogs: []string{
|
||||
nowStr + ` pinniped-login cmd/login_static.go:159 exchanging static token for cluster credential {"endpoint": "https://127.0.0.1/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
|
||||
nowStr + ` cmd/login_static.go:160 exchanging static token for cluster credential {"endpoint": "https://127.0.0.1/", "authenticator type": "webhook", "authenticator name": "test-authenticator"}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -175,7 +175,6 @@ func TestLoginStaticCommand(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
ctx := plog.AddZapOverridesToContext(context.Background(), t, &buf, nil, clocktesting.NewFakeClock(now))
|
||||
|
||||
84
cmd/pinniped/cmd/oidc_client_options.go
Normal file
84
cmd/pinniped/cmd/oidc_client_options.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"go.pinniped.dev/generated/latest/apis/supervisor/idpdiscovery/v1alpha1"
|
||||
"go.pinniped.dev/pkg/oidcclient"
|
||||
)
|
||||
|
||||
// OIDCClientOptions is an interface that wraps the creation of Options for the purpose of making them
|
||||
// more friendly to unit tests. Because the Option type refers to a private struct type, it is hard
|
||||
// to create mocks for them in tests of other packages. This provides a seam that can be mocked.
|
||||
// No need for this interface to include deprecated options (such as WithLogger), since those should never be invoked.
|
||||
type OIDCClientOptions interface {
|
||||
WithContext(ctx context.Context) oidcclient.Option
|
||||
WithLoginLogger(logger oidcclient.Logger) oidcclient.Option
|
||||
WithListenPort(port uint16) oidcclient.Option
|
||||
WithSkipBrowserOpen() oidcclient.Option
|
||||
WithSkipListen() oidcclient.Option
|
||||
WithSkipPrintLoginURL() oidcclient.Option
|
||||
WithSessionCache(cache oidcclient.SessionCache) oidcclient.Option
|
||||
WithClient(httpClient *http.Client) oidcclient.Option
|
||||
WithScopes(scopes []string) oidcclient.Option
|
||||
WithRequestAudience(audience string) oidcclient.Option
|
||||
WithLoginFlow(loginFlow v1alpha1.IDPFlow, flowSource string) oidcclient.Option
|
||||
WithUpstreamIdentityProvider(upstreamName, upstreamType string) oidcclient.Option
|
||||
}
|
||||
|
||||
// clientOptions implements OIDCClientOptions for production use.
|
||||
type clientOptions struct{}
|
||||
|
||||
var _ OIDCClientOptions = (*clientOptions)(nil)
|
||||
|
||||
func (o *clientOptions) WithContext(ctx context.Context) oidcclient.Option {
|
||||
return oidcclient.WithContext(ctx)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithLoginLogger(logger oidcclient.Logger) oidcclient.Option {
|
||||
return oidcclient.WithLoginLogger(logger)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithListenPort(port uint16) oidcclient.Option {
|
||||
return oidcclient.WithListenPort(port)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithSkipBrowserOpen() oidcclient.Option {
|
||||
return oidcclient.WithSkipBrowserOpen()
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithSkipListen() oidcclient.Option {
|
||||
return oidcclient.WithSkipListen()
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithSkipPrintLoginURL() oidcclient.Option {
|
||||
return oidcclient.WithSkipPrintLoginURL()
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithSessionCache(cache oidcclient.SessionCache) oidcclient.Option {
|
||||
return oidcclient.WithSessionCache(cache)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithClient(httpClient *http.Client) oidcclient.Option {
|
||||
return oidcclient.WithClient(httpClient)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithScopes(scopes []string) oidcclient.Option {
|
||||
return oidcclient.WithScopes(scopes)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithRequestAudience(audience string) oidcclient.Option {
|
||||
return oidcclient.WithRequestAudience(audience)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithLoginFlow(loginFlow v1alpha1.IDPFlow, flowSource string) oidcclient.Option {
|
||||
return oidcclient.WithLoginFlow(loginFlow, flowSource)
|
||||
}
|
||||
|
||||
func (o *clientOptions) WithUpstreamIdentityProvider(upstreamName, upstreamType string) oidcclient.Option {
|
||||
return oidcclient.WithUpstreamIdentityProvider(upstreamName, upstreamType)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
@@ -126,7 +126,6 @@ func TestNewVersionCmd(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.getBuildInfo != nil {
|
||||
getBuildInfo = tt.getBuildInfo
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2021-2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2021-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
@@ -24,9 +24,21 @@ import (
|
||||
"go.pinniped.dev/internal/here"
|
||||
)
|
||||
|
||||
type whoamiDeps struct {
|
||||
getenv func(key string) string
|
||||
getClientsets getClientsetsFunc
|
||||
}
|
||||
|
||||
func whoamiRealDeps() whoamiDeps {
|
||||
return whoamiDeps{
|
||||
getenv: os.Getenv,
|
||||
getClientsets: getRealClientsets,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
rootCmd.AddCommand(newWhoamiCommand(getRealConciergeClientset))
|
||||
rootCmd.AddCommand(newWhoamiCommand(whoamiRealDeps()))
|
||||
}
|
||||
|
||||
type whoamiFlags struct {
|
||||
@@ -44,7 +56,7 @@ type clusterInfo struct {
|
||||
url string
|
||||
}
|
||||
|
||||
func newWhoamiCommand(getClientset getConciergeClientsetFunc) *cobra.Command {
|
||||
func newWhoamiCommand(deps whoamiDeps) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Args: cobra.NoArgs, // do not accept positional arguments for this command
|
||||
Use: "whoami",
|
||||
@@ -56,21 +68,21 @@ func newWhoamiCommand(getClientset getConciergeClientsetFunc) *cobra.Command {
|
||||
// flags
|
||||
f := cmd.Flags()
|
||||
f.StringVarP(&flags.outputFormat, "output", "o", "text", "Output format (e.g., 'yaml', 'json', 'text')")
|
||||
f.StringVar(&flags.kubeconfigPath, "kubeconfig", os.Getenv("KUBECONFIG"), "Path to kubeconfig file")
|
||||
f.StringVar(&flags.kubeconfigPath, "kubeconfig", deps.getenv("KUBECONFIG"), "Path to kubeconfig file")
|
||||
f.StringVar(&flags.kubeconfigContextOverride, "kubeconfig-context", "", "Kubeconfig context name (default: current active context)")
|
||||
f.StringVar(&flags.apiGroupSuffix, "api-group-suffix", groupsuffix.PinnipedDefaultSuffix, "Concierge API group suffix")
|
||||
f.DurationVar(&flags.timeout, "timeout", 0, "Timeout for the WhoAmI API request (default: 0, meaning no timeout)")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, _ []string) error {
|
||||
return runWhoami(cmd.OutOrStdout(), getClientset, flags)
|
||||
return runWhoami(cmd.OutOrStdout(), deps, flags)
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func runWhoami(output io.Writer, getClientset getConciergeClientsetFunc, flags *whoamiFlags) error {
|
||||
func runWhoami(output io.Writer, deps whoamiDeps, flags *whoamiFlags) error {
|
||||
clientConfig := newClientConfig(flags.kubeconfigPath, flags.kubeconfigContextOverride)
|
||||
clientset, err := getClientset(clientConfig, flags.apiGroupSuffix)
|
||||
conciergeClient, _, _, err := deps.getClientsets(clientConfig, flags.apiGroupSuffix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not configure Kubernetes client: %w", err)
|
||||
}
|
||||
@@ -96,10 +108,10 @@ func runWhoami(output io.Writer, getClientset getConciergeClientsetFunc, flags *
|
||||
defer cancelFunc()
|
||||
}
|
||||
|
||||
whoAmI, err := clientset.IdentityV1alpha1().WhoAmIRequests().Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
whoAmI, err := conciergeClient.IdentityV1alpha1().WhoAmIRequests().Create(ctx, &identityv1alpha1.WhoAmIRequest{}, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
hint := ""
|
||||
if errors.IsNotFound(err) {
|
||||
if apierrors.IsNotFound(err) {
|
||||
hint = " (is the Pinniped WhoAmI API running and healthy?)"
|
||||
}
|
||||
return fmt.Errorf("could not complete WhoAmIRequest%s: %w", hint, err)
|
||||
|
||||
@@ -1,29 +1,48 @@
|
||||
// Copyright 2023 the Pinniped contributors. All Rights Reserved.
|
||||
// Copyright 2023-2025 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
aggregatorclient "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset"
|
||||
|
||||
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
|
||||
conciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
fakeconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
conciergefake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
"go.pinniped.dev/internal/constable"
|
||||
"go.pinniped.dev/internal/here"
|
||||
)
|
||||
|
||||
func TestWhoami(t *testing.T) {
|
||||
helpOutputFormatString := here.Doc(`
|
||||
Print information about the current user
|
||||
|
||||
Usage:
|
||||
whoami [flags]
|
||||
|
||||
Flags:
|
||||
--api-group-suffix string Concierge API group suffix (default "pinniped.dev")
|
||||
-h, --help help for whoami
|
||||
--kubeconfig string Path to kubeconfig file%s
|
||||
--kubeconfig-context string Kubeconfig context name (default: current active context)
|
||||
-o, --output string Output format (e.g., 'yaml', 'json', 'text') (default "text")
|
||||
--timeout duration Timeout for the WhoAmI API request (default: 0, meaning no timeout)
|
||||
`)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
env map[string]string
|
||||
groupsOverride []string
|
||||
gettingClientsetErr error
|
||||
callingAPIErr error
|
||||
@@ -31,22 +50,17 @@ func TestWhoami(t *testing.T) {
|
||||
wantStdout, wantStderr string
|
||||
}{
|
||||
{
|
||||
name: "help flag",
|
||||
args: []string{"--help"},
|
||||
wantStdout: here.Doc(`
|
||||
Print information about the current user
|
||||
|
||||
Usage:
|
||||
whoami [flags]
|
||||
|
||||
Flags:
|
||||
--api-group-suffix string Concierge API group suffix (default "pinniped.dev")
|
||||
-h, --help help for whoami
|
||||
--kubeconfig string Path to kubeconfig file
|
||||
--kubeconfig-context string Kubeconfig context name (default: current active context)
|
||||
-o, --output string Output format (e.g., 'yaml', 'json', 'text') (default "text")
|
||||
--timeout duration Timeout for the WhoAmI API request (default: 0, meaning no timeout)
|
||||
`),
|
||||
name: "help flag passed",
|
||||
args: []string{"--help"},
|
||||
wantStdout: fmt.Sprintf(helpOutputFormatString, ""),
|
||||
},
|
||||
{
|
||||
name: "help flag passed with KUBECONFIG env var set",
|
||||
env: map[string]string{
|
||||
"KUBECONFIG": "/path/to/kubeconfig",
|
||||
},
|
||||
args: []string{"--help"},
|
||||
wantStdout: fmt.Sprintf(helpOutputFormatString, ` (default "/path/to/kubeconfig")`),
|
||||
},
|
||||
{
|
||||
name: "text output",
|
||||
@@ -107,8 +121,7 @@ func TestWhoami(t *testing.T) {
|
||||
Current user info:
|
||||
|
||||
Username: some-username
|
||||
Groups:
|
||||
`),
|
||||
Groups:` + " \n"), // Linters and codeformatters don't like the extra space after "Groups:" and before the newline
|
||||
},
|
||||
{
|
||||
name: "json output",
|
||||
@@ -274,20 +287,20 @@ func TestWhoami(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "calling API fails because WhoAmI API is not installed",
|
||||
callingAPIErr: errors.NewNotFound(identityv1alpha1.SchemeGroupVersion.WithResource("whoamirequests").GroupResource(), "whatever"),
|
||||
callingAPIErr: apierrors.NewNotFound(identityv1alpha1.SchemeGroupVersion.WithResource("whoamirequests").GroupResource(), "whatever"),
|
||||
wantError: true,
|
||||
wantStderr: "Error: could not complete WhoAmIRequest (is the Pinniped WhoAmI API running and healthy?): whoamirequests.identity.concierge.pinniped.dev \"whatever\" not found\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
getClientset := func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, error) {
|
||||
getClientsetFunc := func(clientConfig clientcmd.ClientConfig, apiGroupSuffix string) (conciergeclientset.Interface, kubernetes.Interface, aggregatorclient.Interface, error) {
|
||||
if test.gettingClientsetErr != nil {
|
||||
return nil, test.gettingClientsetErr
|
||||
return nil, nil, nil, test.gettingClientsetErr
|
||||
}
|
||||
clientset := fakeconciergeclientset.NewSimpleClientset()
|
||||
clientset.PrependReactor("create", "whoamirequests", func(_ kubetesting.Action) (bool, runtime.Object, error) {
|
||||
conciergeClient := conciergefake.NewSimpleClientset()
|
||||
conciergeClient.PrependReactor("create", "whoamirequests", func(_ kubetesting.Action) (bool, runtime.Object, error) {
|
||||
if test.callingAPIErr != nil {
|
||||
return true, nil, test.callingAPIErr
|
||||
}
|
||||
@@ -306,9 +319,15 @@ func TestWhoami(t *testing.T) {
|
||||
},
|
||||
}, nil
|
||||
})
|
||||
return clientset, nil
|
||||
return conciergeClient, nil, nil, nil
|
||||
}
|
||||
cmd := newWhoamiCommand(getClientset)
|
||||
|
||||
cmd := newWhoamiCommand(whoamiDeps{
|
||||
getenv: func(key string) string {
|
||||
return test.env[key]
|
||||
},
|
||||
getClientsets: getClientsetFunc,
|
||||
})
|
||||
|
||||
stdout, stderr := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
|
||||
cmd.SetOut(stdout)
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: jwtauthenticators.authentication.concierge.pinniped.dev
|
||||
spec:
|
||||
group: authentication.concierge.pinniped.dev
|
||||
@@ -25,6 +25,9 @@ spec:
|
||||
- jsonPath: .spec.audience
|
||||
name: Audience
|
||||
type: string
|
||||
- jsonPath: .status.phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
@@ -34,7 +37,6 @@ spec:
|
||||
description: |-
|
||||
JWTAuthenticator describes the configuration of a JWT authenticator.
|
||||
|
||||
|
||||
Upon receiving a signed JWT, a JWTAuthenticator will performs some validation on it (e.g., valid
|
||||
signature, existence of claims, etc.) and extract the username and groups from the token.
|
||||
properties:
|
||||
@@ -92,6 +94,39 @@ spec:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM bundle).
|
||||
If omitted, a default set of system roots will be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- audience
|
||||
@@ -104,16 +139,8 @@ spec:
|
||||
description: Represents the observations of the authenticator's current
|
||||
state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -154,12 +181,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: webhookauthenticators.authentication.concierge.pinniped.dev
|
||||
spec:
|
||||
group: authentication.concierge.pinniped.dev
|
||||
@@ -22,6 +22,9 @@ spec:
|
||||
- jsonPath: .spec.endpoint
|
||||
name: Endpoint
|
||||
type: string
|
||||
- jsonPath: .status.phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
@@ -63,6 +66,39 @@ spec:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM bundle).
|
||||
If omitted, a default set of system roots will be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Concierge is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- endpoint
|
||||
@@ -74,16 +110,8 @@ spec:
|
||||
description: Represents the observations of the authenticator's current
|
||||
state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -124,12 +152,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: credentialissuers.config.concierge.pinniped.dev
|
||||
spec:
|
||||
group: config.concierge.pinniped.dev
|
||||
@@ -61,7 +61,6 @@ spec:
|
||||
ExternalEndpoint describes the HTTPS endpoint where the proxy will be exposed. If not set, the proxy will
|
||||
be served using the external name of the LoadBalancer service or the cluster service DNS name.
|
||||
|
||||
|
||||
This field must be non-empty when spec.impersonationProxy.service.type is "None".
|
||||
type: string
|
||||
mode:
|
||||
@@ -99,7 +98,6 @@ spec:
|
||||
description: |-
|
||||
Type specifies the type of Service to provision for the impersonation proxy.
|
||||
|
||||
|
||||
If the type is "None", then the "spec.impersonationProxy.externalEndpoint" field must be set to a non-empty
|
||||
value so that the Concierge can properly advertise the endpoint in the CredentialIssuer's status.
|
||||
enum:
|
||||
@@ -112,7 +110,6 @@ spec:
|
||||
description: |-
|
||||
TLS contains information about how the Concierge impersonation proxy should serve TLS.
|
||||
|
||||
|
||||
If this field is empty, the impersonation proxy will generate its own TLS certificate.
|
||||
properties:
|
||||
certificateAuthorityData:
|
||||
@@ -137,24 +134,6 @@ spec:
|
||||
status:
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: |-
|
||||
Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
This field is deprecated and will be removed in a future version.
|
||||
properties:
|
||||
certificateAuthorityData:
|
||||
description: The K8s API server CA bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
server:
|
||||
description: The K8s API server URL.
|
||||
minLength: 1
|
||||
pattern: ^https://|^http://
|
||||
type: string
|
||||
required:
|
||||
- certificateAuthorityData
|
||||
- server
|
||||
type: object
|
||||
strategies:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
#! Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
#@ load("@ytt:json", "json")
|
||||
#@ load("helpers.lib.yaml", "defaultLabel", "labels", "deploymentPodLabel", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "getAndValidateLogLevel", "pinnipedDevAPIGroupWithPrefix")
|
||||
#@ load("helpers.lib.yaml",
|
||||
#@ "defaultLabel",
|
||||
#@ "labels",
|
||||
#@ "deploymentPodLabel",
|
||||
#@ "namespace",
|
||||
#@ "defaultResourceName",
|
||||
#@ "defaultResourceNameWithSuffix",
|
||||
#@ "getAndValidateLogLevel",
|
||||
#@ "pinnipedDevAPIGroupWithPrefix",
|
||||
#@ )
|
||||
#@ load("@ytt:template", "template")
|
||||
|
||||
#@ if not data.values.into_namespace:
|
||||
@@ -68,6 +77,7 @@ data:
|
||||
apiGroupSuffix: (@= data.values.api_group_suffix @)
|
||||
# aggregatedAPIServerPort may be set here, although other YAML references to the default port (10250) may also need to be updated
|
||||
# impersonationProxyServerPort may be set here, although other YAML references to the default port (8444) may also need to be updated
|
||||
aggregatedAPIServerDisableAdmissionPlugins: []
|
||||
names:
|
||||
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
|
||||
credentialIssuer: (@= defaultResourceNameWithSuffix("config") @)
|
||||
@@ -83,6 +93,7 @@ data:
|
||||
labels: (@= json.encode(labels()).rstrip() @)
|
||||
kubeCertAgent:
|
||||
namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @)
|
||||
priorityClassName: (@= data.values.kube_cert_agent_priority_class_name @)
|
||||
(@ if data.values.kube_cert_agent_image: @)
|
||||
image: (@= data.values.kube_cert_agent_image @)
|
||||
(@ else: @)
|
||||
@@ -94,12 +105,17 @@ data:
|
||||
(@ end @)
|
||||
(@ if data.values.image_pull_dockerconfigjson: @)
|
||||
imagePullSecrets:
|
||||
- image-pull-secret
|
||||
- image-pull-secret
|
||||
(@ end @)
|
||||
(@ if data.values.log_level: @)
|
||||
log:
|
||||
level: (@= getAndValidateLogLevel() @)
|
||||
(@ end @)
|
||||
(@ end @)
|
||||
tls:
|
||||
onedottwo:
|
||||
allowedCiphers: (@= str(data.values.allowed_ciphers_for_tls_onedottwo) @)
|
||||
audit:
|
||||
logUsernamesAndGroups: (@= data.values.audit.log_usernames_and_groups @)
|
||||
---
|
||||
#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "":
|
||||
apiVersion: v1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ def validate_strings_map(obj):
|
||||
@@ -68,15 +68,24 @@ image_digest: ""
|
||||
image_tag: latest
|
||||
|
||||
#@schema/title "Kube Cert Agent image"
|
||||
#@ kube_cert_agent_image = "Optionally specify a different image for the 'kube-cert-agent' pod which is scheduled \
|
||||
#@ kube_cert_agent_image_desc = "Optionally specify a different image for the 'kube-cert-agent' pod which is scheduled \
|
||||
#@ on the control plane. This image needs only to include `sleep` and `cat` binaries. \
|
||||
#@ By default, the same image specified for image_repo/image_digest/image_tag will be re-used."
|
||||
#@schema/desc kube_cert_agent_image
|
||||
#@schema/desc kube_cert_agent_image_desc
|
||||
#@schema/examples ("Image including tag or digest", "ghcr.io/vmware-tanzu/pinniped/pinniped-server:latest")
|
||||
#@schema/nullable
|
||||
#@schema/validation min_len=1
|
||||
kube_cert_agent_image: ""
|
||||
|
||||
#@schema/title "Kube Cert Agent Priority Class Name"
|
||||
#@ kube_cert_agent_priority_class_name_desc = "Optionally specify a PriorityClassName for the 'kube-cert-agent' pod. \
|
||||
#@ See https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/ for more details. \
|
||||
#@ By default, this is the empty string."
|
||||
#@schema/desc kube_cert_agent_priority_class_name_desc
|
||||
#@schema/examples ("name of a PriorityClass object", "high-priority")
|
||||
#@schema/validation min_len=0
|
||||
kube_cert_agent_priority_class_name: ""
|
||||
|
||||
#@schema/title "Image pull dockerconfigjson"
|
||||
#@ image_pull_dockerconfigjson_desc = "A base64 encoded secret to be used when pulling the `image_repo` container image. \
|
||||
#@ Can be used when the image_repo is a private registry. Typically, the value would be the output of: \
|
||||
@@ -214,3 +223,32 @@ https_proxy: ""
|
||||
#@ localhost endpoints, and the known instance metadata IP address for public cloud providers."
|
||||
#@schema/desc no_proxy_desc
|
||||
no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local"
|
||||
|
||||
#@schema/title "Allowed Ciphers for TLS 1.2"
|
||||
#@ allowed_ciphers_for_tls_onedottwo_desc = "When specified, only the ciphers listed will be used for TLS 1.2. \
|
||||
#@ This includes both server-side and client-side TLS connections. \
|
||||
#@ This list must only include cipher suites that Pinniped is configured to accept \
|
||||
#@ (see internal/crypto/ptls/profiles.go and internal/crypto/ptls/profiles_fips_strict.go). \
|
||||
#@ Allowing too few ciphers may cause critical parts of Pinniped to be unable to function. For example, \
|
||||
#@ Kubernetes pod readiness checks, Pinniped pods acting as a client to the Kubernetes API server, \
|
||||
#@ Pinniped pods acting as a client to external identity providers, or Pinniped pods acting as an APIService server \
|
||||
#@ all need to be able to function with the allowed TLS cipher suites. \
|
||||
#@ An empty array means accept Pinniped's defaults."
|
||||
#@schema/desc allowed_ciphers_for_tls_onedottwo_desc
|
||||
#@schema/examples ("Example with a few secure ciphers", ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"])
|
||||
#! No type, default, or validation is required here.
|
||||
#! An empty array is perfectly valid, as is any array of strings.
|
||||
allowed_ciphers_for_tls_onedottwo:
|
||||
- ""
|
||||
|
||||
#@schema/title "Audit logging configuration"
|
||||
#@schema/desc "Customize the content of audit log events."
|
||||
audit:
|
||||
|
||||
#@schema/title "Log usernames and groups"
|
||||
#@ log_usernames_and_groups_desc = "Enables or disables printing usernames and group names in audit logs. Options are 'enabled' or 'disabled'. \
|
||||
#@ If enabled, usernames are group names may be printed in audit log events. \
|
||||
#@ If disabled, usernames and group names will be redacted from audit logs because they might contain personally identifiable information."
|
||||
#@schema/desc log_usernames_and_groups_desc
|
||||
#@schema/validation one_of=["enabled", "disabled"]
|
||||
log_usernames_and_groups: disabled
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: federationdomains.config.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: config.supervisor.pinniped.dev
|
||||
@@ -55,7 +55,6 @@ spec:
|
||||
description: |-
|
||||
IdentityProviders is the list of identity providers available for use by this FederationDomain.
|
||||
|
||||
|
||||
An identity provider CR (e.g. OIDCIdentityProvider or LDAPIdentityProvider) describes how to connect to a server,
|
||||
how to talk in a specific protocol for authentication, and how to use the schema of that server/protocol to
|
||||
extract a normalized user identity. Normalized user identities include a username and a list of group names.
|
||||
@@ -68,7 +67,6 @@ spec:
|
||||
the authentication to the Kubernetes clusters that belong to this FederationDomain. For example, a policy could
|
||||
disallow the authentication unless the user belongs to a specific group in the identity provider.
|
||||
|
||||
|
||||
For backwards compatibility with versions of Pinniped which predate support for multiple identity providers,
|
||||
an empty IdentityProviders list will cause the FederationDomain to use all available identity providers which
|
||||
exist in the same namespace, but also to reject all authentication requests when there is more than one identity
|
||||
@@ -143,8 +141,9 @@ spec:
|
||||
Type is "string", and is otherwise ignored.
|
||||
type: string
|
||||
type:
|
||||
description: Type determines the type of the constant,
|
||||
and indicates which other field should be non-empty.
|
||||
description: |-
|
||||
Type determines the type of the constant, and indicates which other field should be non-empty.
|
||||
Allowed values are "string" or "stringList".
|
||||
enum:
|
||||
- string
|
||||
- stringList
|
||||
@@ -222,14 +221,12 @@ spec:
|
||||
https://github.com/google/cel-spec/blob/master/doc/langdef.md plus the CEL string extensions defined in
|
||||
https://github.com/google/cel-go/tree/master/ext#strings.
|
||||
|
||||
|
||||
The username and groups extracted from the identity provider, and the constants defined in this CR, are
|
||||
available as variables in all expressions. The username is provided via a variable called `username` and
|
||||
the list of group names is provided via a variable called `groups` (which may be an empty list).
|
||||
Each user-provided constants is provided via a variable named `strConst.varName` for string constants
|
||||
and `strListConst.varName` for string list constants.
|
||||
|
||||
|
||||
The only allowed types for expressions are currently policy/v1, username/v1, and groups/v1.
|
||||
Each policy/v1 must return a boolean, and when it returns false, no more expressions from the list are evaluated
|
||||
and the authentication attempt is rejected.
|
||||
@@ -242,7 +239,6 @@ spec:
|
||||
Transformations of type groups/v1 do not return usernames, and therefore cannot change the usernames.
|
||||
After each expression, the new (potentially changed) username or groups get passed to the following expression.
|
||||
|
||||
|
||||
Any compilation or static type-checking failure of any expression will cause an error status on the FederationDomain.
|
||||
During an authentication attempt, any unexpected runtime evaluation errors (e.g. division by zero) cause the
|
||||
authentication attempt to fail. When all expressions evaluate successfully, then the (potentially changed) username
|
||||
@@ -262,8 +258,9 @@ spec:
|
||||
an authentication attempt. When empty, a default message will be used.
|
||||
type: string
|
||||
type:
|
||||
description: Type determines the type of the expression.
|
||||
It must be one of the supported types.
|
||||
description: |-
|
||||
Type determines the type of the expression. It must be one of the supported types.
|
||||
Allowed values are "policy/v1", "username/v1", or "groups/v1".
|
||||
enum:
|
||||
- policy/v1
|
||||
- username/v1
|
||||
@@ -288,11 +285,13 @@ spec:
|
||||
https://example.com/foo, then your authorization endpoint will look like
|
||||
https://example.com/foo/some/path/to/auth/endpoint).
|
||||
|
||||
|
||||
See
|
||||
https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.3 for more information.
|
||||
minLength: 1
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: issuer must be an HTTPS URL
|
||||
rule: isURL(self) && url(self).getScheme() == 'https'
|
||||
tls:
|
||||
description: TLS specifies a secret which will contain Transport Layer
|
||||
Security (TLS) configuration for the FederationDomain.
|
||||
@@ -304,21 +303,17 @@ spec:
|
||||
named here must contain keys named `tls.crt` and `tls.key` that contain the certificate and private key to use
|
||||
for TLS.
|
||||
|
||||
|
||||
Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) supported by all major browsers.
|
||||
|
||||
|
||||
SecretName is required if you would like to use different TLS certificates for issuers of different hostnames.
|
||||
SNI requests do not include port numbers, so all issuers with the same DNS hostname must use the same
|
||||
SecretName value even if they have different port numbers.
|
||||
|
||||
|
||||
SecretName is not required when you would like to use only the HTTP endpoints (e.g. when the HTTP listener is
|
||||
configured to listen on loopback interfaces or UNIX domain sockets for traffic from a service mesh sidecar).
|
||||
It is also not required when you would like all requests to this OIDC Provider's HTTPS endpoints to
|
||||
use the default TLS certificate, which is configured elsewhere.
|
||||
|
||||
|
||||
When your Issuer URL's host is an IP address, then this field is ignored. SNI does not work for IP addresses.
|
||||
type: string
|
||||
type: object
|
||||
@@ -332,16 +327,8 @@ spec:
|
||||
description: Conditions represent the observations of an FederationDomain's
|
||||
current state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -382,12 +369,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
@@ -421,10 +403,13 @@ spec:
|
||||
exist.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -434,10 +419,13 @@ spec:
|
||||
encrypting state parameters is stored.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -447,10 +435,13 @@ spec:
|
||||
signing state parameters is stored.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
@@ -460,10 +451,13 @@ spec:
|
||||
signing tokens is stored.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
TODO: Add other useful fields. apiVersion, kind, uid?
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: oidcclients.config.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: config.supervisor.pinniped.dev
|
||||
@@ -59,7 +59,6 @@ spec:
|
||||
allowedGrantTypes is a list of the allowed grant_type param values that should be accepted during OIDC flows with this
|
||||
client.
|
||||
|
||||
|
||||
Must only contain the following values:
|
||||
- authorization_code: allows the client to perform the authorization code grant flow, i.e. allows the webapp to
|
||||
authenticate users. This grant must always be listed.
|
||||
@@ -93,7 +92,6 @@ spec:
|
||||
description: |-
|
||||
allowedScopes is a list of the allowed scopes param values that should be accepted during OIDC flows with this client.
|
||||
|
||||
|
||||
Must only contain the following values:
|
||||
- openid: The client is allowed to request ID tokens. ID tokens only include the required claims by default (iss, sub, aud, exp, iat).
|
||||
This scope must always be listed.
|
||||
@@ -152,16 +150,8 @@ spec:
|
||||
description: conditions represent the observations of an OIDCClient's
|
||||
current state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -202,12 +192,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
@@ -13,7 +13,7 @@
|
||||
#@ "pinnipedDevAPIGroupWithPrefix",
|
||||
#@ "getPinnipedConfigMapData",
|
||||
#@ "hasUnixNetworkEndpoint",
|
||||
#@ )
|
||||
#@ )
|
||||
#@ load("@ytt:template", "template")
|
||||
|
||||
#@ if not data.values.into_namespace:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2025 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:data", "data")
|
||||
@@ -53,6 +53,16 @@ _: #@ template.replace(data.values.custom_labels)
|
||||
#@ "apiService": defaultResourceNameWithSuffix("api"),
|
||||
#@ },
|
||||
#@ "labels": labels(),
|
||||
#@ "tls": {
|
||||
#@ "onedottwo": {
|
||||
#@ "allowedCiphers": data.values.allowed_ciphers_for_tls_onedottwo
|
||||
#@ }
|
||||
#@ },
|
||||
#@ "audit": {
|
||||
#@ "logUsernamesAndGroups": data.values.audit.log_usernames_and_groups,
|
||||
#@ "logInternalPaths": data.values.audit.log_internal_paths
|
||||
#@ },
|
||||
#@ "aggregatedAPIServerDisableAdmissionPlugins": []
|
||||
#@ }
|
||||
#@ if data.values.log_level:
|
||||
#@ config["log"] = {}
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: activedirectoryidentityproviders.idp.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: idp.supervisor.pinniped.dev
|
||||
@@ -125,21 +125,18 @@ spec:
|
||||
to keep the groups observed in Kubernetes clusters in-sync with the identity
|
||||
provider.
|
||||
|
||||
|
||||
In some environments, frequent group membership queries may result in a
|
||||
significant performance impact on the identity provider and/or the supervisor.
|
||||
The best approach to handle performance impacts is to tweak the group query
|
||||
to be more performant, for example by disabling nested group search or by
|
||||
using a more targeted group search base.
|
||||
|
||||
|
||||
If the group search query cannot be made performant and you are willing to
|
||||
have group memberships remain static for approximately a day, then set
|
||||
skipGroupRefresh to true. This is an insecure configuration as authorization
|
||||
policies that are bound to group membership will not notice if a user has
|
||||
been removed from a particular group until their next login.
|
||||
|
||||
|
||||
This is an experimental feature that may be removed or significantly altered
|
||||
in the future. Consumers of this configuration should carefully read all
|
||||
release notes before upgrading to ensure that the meaning of this field has
|
||||
@@ -170,6 +167,39 @@ spec:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM bundle).
|
||||
If omitted, a default set of system roots will be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
userSearch:
|
||||
description: UserSearch contains the configuration for searching for
|
||||
@@ -228,16 +258,8 @@ spec:
|
||||
description: Represents the observations of an identity provider's
|
||||
current state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -278,12 +300,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: githubidentityproviders.idp.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: idp.supervisor.pinniped.dev
|
||||
names:
|
||||
categories:
|
||||
- pinniped
|
||||
- pinniped-idp
|
||||
- pinniped-idps
|
||||
kind: GitHubIdentityProvider
|
||||
listKind: GitHubIdentityProviderList
|
||||
plural: githubidentityproviders
|
||||
singular: githubidentityprovider
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.githubAPI.host
|
||||
name: Host
|
||||
type: string
|
||||
- jsonPath: .status.phase
|
||||
name: Status
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: |-
|
||||
GitHubIdentityProvider describes the configuration of an upstream GitHub identity provider.
|
||||
This upstream provider can be configured with either a GitHub App or a GitHub OAuth2 App.
|
||||
|
||||
Right now, only web-based logins are supported, for both the pinniped-cli client and clients configured
|
||||
as OIDCClients.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: Spec for configuring the identity provider.
|
||||
properties:
|
||||
allowAuthentication:
|
||||
description: AllowAuthentication allows customization of who can authenticate
|
||||
using this IDP and how.
|
||||
properties:
|
||||
organizations:
|
||||
description: Organizations allows customization of which organizations
|
||||
can authenticate using this IDP.
|
||||
properties:
|
||||
allowed:
|
||||
description: |-
|
||||
Allowed, when specified, indicates that only users with membership in at least one of the listed
|
||||
GitHub organizations may log in. In addition, the group membership presented to Kubernetes will only include
|
||||
teams within the listed GitHub organizations. Additional login rules or group filtering can optionally be
|
||||
provided as policy expression on any Pinniped Supervisor FederationDomain that includes this IDP.
|
||||
|
||||
The configured GitHub App or GitHub OAuth App must be allowed to see membership in the listed organizations,
|
||||
otherwise Pinniped will not be aware that the user belongs to the listed organization or any teams
|
||||
within that organization.
|
||||
|
||||
If no organizations are listed, you must set organizations: AllGitHubUsers.
|
||||
items:
|
||||
type: string
|
||||
maxItems: 64
|
||||
type: array
|
||||
x-kubernetes-list-type: set
|
||||
policy:
|
||||
default: OnlyUsersFromAllowedOrganizations
|
||||
description: |-
|
||||
Allowed values are "OnlyUsersFromAllowedOrganizations" or "AllGitHubUsers".
|
||||
Defaults to "OnlyUsersFromAllowedOrganizations".
|
||||
|
||||
Must be set to "AllGitHubUsers" if the allowed field is empty.
|
||||
|
||||
This field only exists to ensure that Pinniped administrators are aware that an empty list of
|
||||
allowedOrganizations means all GitHub users are allowed to log in.
|
||||
enum:
|
||||
- OnlyUsersFromAllowedOrganizations
|
||||
- AllGitHubUsers
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: spec.allowAuthentication.organizations.policy must
|
||||
be 'OnlyUsersFromAllowedOrganizations' when spec.allowAuthentication.organizations.allowed
|
||||
has organizations listed
|
||||
rule: '!(has(self.allowed) && size(self.allowed) > 0 && self.policy
|
||||
== ''AllGitHubUsers'')'
|
||||
- message: spec.allowAuthentication.organizations.policy must
|
||||
be 'AllGitHubUsers' when spec.allowAuthentication.organizations.allowed
|
||||
is empty
|
||||
rule: '!((!has(self.allowed) || size(self.allowed) == 0) &&
|
||||
self.policy == ''OnlyUsersFromAllowedOrganizations'')'
|
||||
required:
|
||||
- organizations
|
||||
type: object
|
||||
claims:
|
||||
default: {}
|
||||
description: Claims allows customization of the username and groups
|
||||
claims.
|
||||
properties:
|
||||
groups:
|
||||
default: slug
|
||||
description: |-
|
||||
Groups configures which property of the GitHub team record shall determine the group names in Kubernetes.
|
||||
|
||||
Can be either "name" or "slug". Defaults to "slug".
|
||||
|
||||
GitHub team names can contain upper and lower case characters, whitespace, and punctuation (e.g. "Kube admins!").
|
||||
|
||||
GitHub team slugs are lower case alphanumeric characters and may contain dashes and underscores (e.g. "kube-admins").
|
||||
|
||||
Group names as presented to Kubernetes will always be prefixed by the GitHub organization name followed by a
|
||||
forward slash (e.g. "my-org/my-team"). GitHub organization login names can only contain alphanumeric characters
|
||||
or single hyphens, so the first forward slash `/` will be the separator between the organization login name and
|
||||
the team name or slug.
|
||||
|
||||
If desired, an admin could configure identity transformation expressions on the Pinniped Supervisor's
|
||||
FederationDomain to further customize how these group names are presented to Kubernetes.
|
||||
|
||||
See the response schema for
|
||||
[List teams for the authenticated user](https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams-for-the-authenticated-user).
|
||||
enum:
|
||||
- name
|
||||
- slug
|
||||
type: string
|
||||
username:
|
||||
default: login:id
|
||||
description: |-
|
||||
Username configures which property of the GitHub user record shall determine the username in Kubernetes.
|
||||
|
||||
Can be either "id", "login", or "login:id". Defaults to "login:id".
|
||||
|
||||
GitHub's user login attributes can only contain alphanumeric characters and non-repeating hyphens,
|
||||
and may not start or end with hyphens. GitHub users are allowed to change their login name,
|
||||
although it is inconvenient. If a GitHub user changed their login name from "foo" to "bar",
|
||||
then a second user might change their name from "baz" to "foo" in order to take the old
|
||||
username of the first user. For this reason, it is not as safe to make authorization decisions
|
||||
based only on the user's login attribute.
|
||||
|
||||
If desired, an admin could configure identity transformation expressions on the Pinniped Supervisor's
|
||||
FederationDomain to further customize how these usernames are presented to Kubernetes.
|
||||
|
||||
Defaults to "login:id", which is the user login attribute, followed by a colon, followed by the unique and
|
||||
unchanging integer ID number attribute. This blends human-readable login names with the unchanging ID value
|
||||
from GitHub. Colons are not allowed in GitHub login attributes or ID numbers, so this is a reasonable
|
||||
choice to concatenate the two values.
|
||||
|
||||
See the response schema for
|
||||
[Get the authenticated user](https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user).
|
||||
enum:
|
||||
- id
|
||||
- login
|
||||
- login:id
|
||||
type: string
|
||||
type: object
|
||||
client:
|
||||
description: Client identifies the secret with credentials for a GitHub
|
||||
App or GitHub OAuth2 App (a GitHub client).
|
||||
properties:
|
||||
secretName:
|
||||
description: |-
|
||||
SecretName contains the name of a namespace-local Secret object that provides the clientID and
|
||||
clientSecret for an GitHub App or GitHub OAuth2 client.
|
||||
|
||||
This secret must be of type "secrets.pinniped.dev/github-client" with keys "clientID" and "clientSecret".
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
type: object
|
||||
githubAPI:
|
||||
default: {}
|
||||
description: GitHubAPI allows configuration for GitHub Enterprise
|
||||
Server
|
||||
properties:
|
||||
host:
|
||||
default: github.com
|
||||
description: |-
|
||||
Host is required only for GitHub Enterprise Server.
|
||||
Defaults to using GitHub's public API ("github.com").
|
||||
For convenience, specifying "github.com" is equivalent to specifying "api.github.com".
|
||||
Do not specify a protocol or scheme since "https://" will always be used.
|
||||
Port is optional. Do not specify a path, query, fragment, or userinfo.
|
||||
Only specify domain name or IP address, subdomains (optional), and port (optional).
|
||||
IPv4 and IPv6 are supported. If using an IPv6 address with a port, you must enclose the IPv6 address
|
||||
in square brackets. Example: "[::1]:443".
|
||||
minLength: 1
|
||||
type: string
|
||||
tls:
|
||||
description: |-
|
||||
TLS configuration for GitHub Enterprise Server.
|
||||
Note that this field should not be needed when using GitHub's public API ("github.com").
|
||||
However, if you choose to specify this field when using GitHub's public API, you must
|
||||
specify a CA bundle that will verify connections to "api.github.com".
|
||||
properties:
|
||||
certificateAuthorityData:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM
|
||||
bundle). If omitted, a default set of system roots will
|
||||
be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- allowAuthentication
|
||||
- client
|
||||
type: object
|
||||
status:
|
||||
description: Status of the identity provider.
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions represents the observations of an identity
|
||||
provider's current state.
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
phase:
|
||||
default: Pending
|
||||
description: Phase summarizes the overall status of the GitHubIdentityProvider.
|
||||
enum:
|
||||
- Pending
|
||||
- Ready
|
||||
- Error
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: ldapidentityproviders.idp.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: idp.supervisor.pinniped.dev
|
||||
@@ -116,21 +116,18 @@ spec:
|
||||
to keep the groups observed in Kubernetes clusters in-sync with the identity
|
||||
provider.
|
||||
|
||||
|
||||
In some environments, frequent group membership queries may result in a
|
||||
significant performance impact on the identity provider and/or the supervisor.
|
||||
The best approach to handle performance impacts is to tweak the group query
|
||||
to be more performant, for example by disabling nested group search or by
|
||||
using a more targeted group search base.
|
||||
|
||||
|
||||
If the group search query cannot be made performant and you are willing to
|
||||
have group memberships remain static for approximately a day, then set
|
||||
skipGroupRefresh to true. This is an insecure configuration as authorization
|
||||
policies that are bound to group membership will not notice if a user has
|
||||
been removed from a particular group until their next login.
|
||||
|
||||
|
||||
This is an experimental feature that may be removed or significantly altered
|
||||
in the future. Consumers of this configuration should carefully read all
|
||||
release notes before upgrading to ensure that the meaning of this field has
|
||||
@@ -161,6 +158,39 @@ spec:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM bundle).
|
||||
If omitted, a default set of system roots will be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
userSearch:
|
||||
description: UserSearch contains the configuration for searching for
|
||||
@@ -219,16 +249,8 @@ spec:
|
||||
description: Represents the observations of an identity provider's
|
||||
current state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -269,12 +291,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.14.0
|
||||
controller-gen.kubebuilder.io/version: v0.18.0
|
||||
name: oidcidentityproviders.idp.supervisor.pinniped.dev
|
||||
spec:
|
||||
group: idp.supervisor.pinniped.dev
|
||||
@@ -211,6 +211,39 @@ spec:
|
||||
description: X.509 Certificate Authority (base64-encoded PEM bundle).
|
||||
If omitted, a default set of system roots will be trusted.
|
||||
type: string
|
||||
certificateAuthorityDataSource:
|
||||
description: |-
|
||||
Reference to a CA bundle in a secret or a configmap.
|
||||
Any changes to the CA bundle in the secret or configmap will be dynamically reloaded.
|
||||
properties:
|
||||
key:
|
||||
description: |-
|
||||
Key is the key name within the secret or configmap from which to read the CA bundle.
|
||||
The value found at this key in the secret or configmap must not be empty, and must be a valid PEM-encoded
|
||||
certificate bundle.
|
||||
minLength: 1
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind configures whether the CA bundle is being sourced from a Kubernetes secret or a configmap.
|
||||
Allowed values are "Secret" or "ConfigMap".
|
||||
"ConfigMap" uses a Kubernetes configmap to source CA Bundles.
|
||||
"Secret" uses Kubernetes secrets of type kubernetes.io/tls or Opaque to source CA Bundles.
|
||||
enum:
|
||||
- Secret
|
||||
- ConfigMap
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the resource name of the secret or configmap from which to read the CA bundle.
|
||||
The referenced secret or configmap must be created in the same namespace where Pinniped Supervisor is installed.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- client
|
||||
@@ -223,16 +256,8 @@ spec:
|
||||
description: Represents the observations of an identity provider's
|
||||
current state.
|
||||
items:
|
||||
description: "Condition contains details for one aspect of the current
|
||||
state of this API Resource.\n---\nThis struct is intended for
|
||||
direct use as an array at the field path .status.conditions. For
|
||||
example,\n\n\n\ttype FooStatus struct{\n\t // Represents the
|
||||
observations of a foo's current state.\n\t // Known .status.conditions.type
|
||||
are: \"Available\", \"Progressing\", and \"Degraded\"\n\t //
|
||||
+patchMergeKey=type\n\t // +patchStrategy=merge\n\t // +listType=map\n\t
|
||||
\ // +listMapKey=type\n\t Conditions []metav1.Condition `json:\"conditions,omitempty\"
|
||||
patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
|
||||
\ // other fields\n\t}"
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
@@ -273,12 +298,7 @@ spec:
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: |-
|
||||
type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
---
|
||||
Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
|
||||
useful (see .node.status.conditions), the ability to deconflict is important.
|
||||
The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
|
||||
@@ -16,6 +16,9 @@ rules:
|
||||
- apiGroups: [""]
|
||||
resources: [secrets]
|
||||
verbs: [create, get, list, patch, update, watch, delete]
|
||||
- apiGroups: [""]
|
||||
resources: [configmaps]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups:
|
||||
- #@ pinnipedDevAPIGroupWithPrefix("config.supervisor")
|
||||
resources: [federationdomains]
|
||||
@@ -56,6 +59,14 @@ rules:
|
||||
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
|
||||
resources: [activedirectoryidentityproviders/status]
|
||||
verbs: [get, patch, update]
|
||||
- apiGroups:
|
||||
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
|
||||
resources: [githubidentityproviders]
|
||||
verbs: [get, list, watch]
|
||||
- apiGroups:
|
||||
- #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
|
||||
resources: [githubidentityproviders/status]
|
||||
verbs: [get, patch, update]
|
||||
#! We want to be able to read pods/replicasets/deployment so we can learn who our deployment is to set
|
||||
#! as an owner reference.
|
||||
- apiGroups: [""]
|
||||
|
||||
@@ -203,3 +203,40 @@ no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.
|
||||
#@schema/nullable
|
||||
#@schema/validation ("a map with keys 'http' and 'https', whose values are either the string 'disabled' or a map having keys 'network' and 'address', and the value of 'network' must be one of the allowed values", validate_endpoints)
|
||||
endpoints: { }
|
||||
|
||||
#@schema/title "Allowed Ciphers for TLS 1.2"
|
||||
#@ allowed_ciphers_for_tls_onedottwo_desc = "When specified, only the ciphers listed will be used for TLS 1.2. \
|
||||
#@ This includes both server-side and client-side TLS connections. \
|
||||
#@ This list must only include cipher suites that Pinniped is configured to accept \
|
||||
#@ (see internal/crypto/ptls/profiles.go and internal/crypto/ptls/profiles_fips_strict.go). \
|
||||
#@ Allowing too few ciphers may cause critical parts of Pinniped to be unable to function. For example, \
|
||||
#@ Kubernetes pod readiness checks, Pinniped pods acting as a client to the Kubernetes API server, \
|
||||
#@ Pinniped pods acting as a client to external identity providers, or Pinniped pods acting as an APIService server \
|
||||
#@ all need to be able to function with the allowed TLS cipher suites. \
|
||||
#@ An empty array means accept Pinniped's defaults."
|
||||
#@schema/desc allowed_ciphers_for_tls_onedottwo_desc
|
||||
#@schema/examples ("Example with a few secure ciphers", ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"])
|
||||
#! No type, default, or validation is required here.
|
||||
#! An empty array is perfectly valid, as is any array of strings.
|
||||
allowed_ciphers_for_tls_onedottwo:
|
||||
- ""
|
||||
|
||||
#@schema/title "Audit logging configuration"
|
||||
#@schema/desc "Customize the content of audit log events."
|
||||
audit:
|
||||
|
||||
#@schema/title "Log usernames and groups"
|
||||
#@ log_usernames_and_groups_desc = "Enables or disables printing usernames and group names in audit logs. Options are 'enabled' or 'disabled'. \
|
||||
#@ If enabled, usernames are group names may be printed in audit log events. \
|
||||
#@ If disabled, usernames and group names will be redacted from audit logs because they might contain personally identifiable information."
|
||||
#@schema/desc log_usernames_and_groups_desc
|
||||
#@schema/validation one_of=["enabled", "disabled"]
|
||||
log_usernames_and_groups: disabled
|
||||
|
||||
#@schema/title "Log HTTPS requests for internal paths"
|
||||
#@ log_internal_paths = "Enables or disables request logging for internal paths in audit logs. Options are 'enabled' or 'disabled'. \
|
||||
#@ If enabled, requests to certain paths that are typically only used internal to the cluster (e.g. /healthz) will be enabled, which can be very verbose. \
|
||||
#@ If disabled, requests to those paths will not be audit logged."
|
||||
#@schema/desc log_internal_paths
|
||||
#@schema/validation one_of=["enabled", "disabled"]
|
||||
log_internal_paths: disabled
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
#! Copyright 2020-2024 the Pinniped contributors. All Rights Reserved.
|
||||
#! SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#@ load("@ytt:overlay", "overlay")
|
||||
@@ -41,6 +41,15 @@ metadata:
|
||||
spec:
|
||||
group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
|
||||
|
||||
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"githubidentityproviders.idp.supervisor.pinniped.dev"}}), expects=1
|
||||
---
|
||||
metadata:
|
||||
#@overlay/match missing_ok=True
|
||||
labels: #@ labels()
|
||||
name: #@ pinnipedDevAPIGroupWithPrefix("githubidentityproviders.idp.supervisor")
|
||||
spec:
|
||||
group: #@ pinnipedDevAPIGroupWithPrefix("idp.supervisor")
|
||||
|
||||
#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"oidcclients.config.supervisor.pinniped.dev"}}), expects=1
|
||||
---
|
||||
metadata:
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// Configuration for configuring TLS on various authenticators.
|
||||
type TLSSpec struct {
|
||||
// X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted.
|
||||
// +optional
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"`
|
||||
}
|
||||
9
generated/1.24/apis/go.mod
generated
9
generated/1.24/apis/go.mod
generated
@@ -1,9 +0,0 @@
|
||||
// This go.mod file is generated by ./hack/update.sh.
|
||||
module go.pinniped.dev/generated/1.24/apis
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
k8s.io/api v0.24.17
|
||||
k8s.io/apimachinery v0.24.17
|
||||
)
|
||||
262
generated/1.24/apis/go.sum
generated
262
generated/1.24/apis/go.sum
generated
@@ -1,262 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0 h1:QK40JKJyMdUDz+h+xvCsru/bJhvG0UxvePV0ufL/AcE=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.24.17 h1:ILPpMleNDZbMJwopUBOVWtmCq3xBAj/4gJEUicy6QGs=
|
||||
k8s.io/api v0.24.17/go.mod h1:Ff5rnpz9qMj3/tXXA504wdk7Mf9zW3JSNWp5tf80VMQ=
|
||||
k8s.io/apimachinery v0.24.17 h1:mewWCeZ3Swr4EAfatVAhHXJHGzCHojphWA/5UJW4pPY=
|
||||
k8s.io/apimachinery v0.24.17/go.mod h1:kSzhCwldu9XB172NDdLffRN0sJ3x95RR7Bmyc4SHhs0=
|
||||
k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc=
|
||||
k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk=
|
||||
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
|
||||
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87JJaPNLh5tIzYHgStQ9Y=
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
@@ -1,4 +0,0 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright 2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// Configuration for TLS parameters related to identity provider integration.
|
||||
type TLSSpec struct {
|
||||
// X.509 Certificate Authority (base64-encoded PEM bundle). If omitted, a default set of system roots will be trusted.
|
||||
// +optional
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData,omitempty"`
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user