mirror of
https://github.com/kubescape/kubescape.git
synced 2026-03-02 01:30:19 +00:00
Compare commits
1904 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c717a9233b | ||
|
|
e37f47de3a | ||
|
|
2ce37bd66e | ||
|
|
13c760c116 | ||
|
|
c6261e45a8 | ||
|
|
0c06b6c3e6 | ||
|
|
18a9ac3d6e | ||
|
|
2bfe2a590c | ||
|
|
fb54f4e6cf | ||
|
|
9025ba5537 | ||
|
|
0c23579db7 | ||
|
|
a755f365df | ||
|
|
15f7b9f954 | ||
|
|
92a2704fa6 | ||
|
|
a3defe3025 | ||
|
|
2be0ef48d8 | ||
|
|
c97513e4e8 | ||
|
|
1757c891aa | ||
|
|
b02410184e | ||
|
|
b4a6a18a56 | ||
|
|
13ca0027a2 | ||
|
|
93b626bb1e | ||
|
|
6b4310cd88 | ||
|
|
c883a297b3 | ||
|
|
3af351d91f | ||
|
|
93cde0f1a0 | ||
|
|
0a5715393c | ||
|
|
9a1cc33efa | ||
|
|
02720d32dd | ||
|
|
ebada00cf1 | ||
|
|
3b68fc94d1 | ||
|
|
10d534b5bf | ||
|
|
2d740fbf4f | ||
|
|
d0728676ee | ||
|
|
8856c84a17 | ||
|
|
0c87ff6843 | ||
|
|
a48d9be386 | ||
|
|
3c93c2c45c | ||
|
|
77e0a04c99 | ||
|
|
b8762b924c | ||
|
|
025e75213a | ||
|
|
c39683872e | ||
|
|
1a3a58a309 | ||
|
|
19438e6143 | ||
|
|
284c8c737b | ||
|
|
3441a65290 | ||
|
|
773e43b1e1 | ||
|
|
ddc0b2daf2 | ||
|
|
596686602c | ||
|
|
5bb0c97f8f | ||
|
|
256db4abfb | ||
|
|
3546961a5e | ||
|
|
e6dc7c2367 | ||
|
|
07fa3b4589 | ||
|
|
d6ed4b1aca | ||
|
|
69846bb4c0 | ||
|
|
2e5ad85fe0 | ||
|
|
1025431d64 | ||
|
|
1a863473e7 | ||
|
|
28a44ac531 | ||
|
|
cf484c328b | ||
|
|
668514e08d | ||
|
|
dc45efb6ef | ||
|
|
6d3844f187 | ||
|
|
4d6e85d4c7 | ||
|
|
d336f4484c | ||
|
|
bf263d8d51 | ||
|
|
cc3cf1932c | ||
|
|
6a4dc79689 | ||
|
|
8c189f6e3c | ||
|
|
f1514d6e76 | ||
|
|
3a038c9a0e | ||
|
|
b4bdf4d860 | ||
|
|
b309cfca7a | ||
|
|
c4b3ef5b80 | ||
|
|
aba978e94a | ||
|
|
a49781e9a8 | ||
|
|
3ba19f55f1 | ||
|
|
40a9b9406d | ||
|
|
d6b8f5862f | ||
|
|
09f13c05e1 | ||
|
|
b1c8872a29 | ||
|
|
22052f5869 | ||
|
|
afce43add6 | ||
|
|
4752364699 | ||
|
|
08e7108dc0 | ||
|
|
108a2d6dd8 | ||
|
|
2c28286bb1 | ||
|
|
79858b7ed7 | ||
|
|
bb2e83eb3b | ||
|
|
282a29b971 | ||
|
|
60b9edc463 | ||
|
|
0f9a5e3127 | ||
|
|
7c79c14363 | ||
|
|
fe84225252 | ||
|
|
56da8d8d92 | ||
|
|
f135e95d2c | ||
|
|
db34183fc1 | ||
|
|
8f3af71c84 | ||
|
|
116aee0c9c | ||
|
|
e5d44f741d | ||
|
|
f005cb7f80 | ||
|
|
9ae9d35ccb | ||
|
|
cb38a4e8a1 | ||
|
|
eb6d39be42 | ||
|
|
3160d74c42 | ||
|
|
5076c38482 | ||
|
|
73c55fe253 | ||
|
|
f48f81c0b5 | ||
|
|
81c1c29b7c | ||
|
|
874aa38f68 | ||
|
|
b9caaf5025 | ||
|
|
61c120de0e | ||
|
|
de3408bf57 | ||
|
|
8d32032ec1 | ||
|
|
42ed787f7b | ||
|
|
ccdba85b3c | ||
|
|
c59f7691dc | ||
|
|
cf87c2d30b | ||
|
|
b547814dec | ||
|
|
b476a72e04 | ||
|
|
4f6f85710a | ||
|
|
47c23de160 | ||
|
|
bc85844ec0 | ||
|
|
134d854722 | ||
|
|
e3522c19cc | ||
|
|
967fc3fe81 | ||
|
|
896a0699ec | ||
|
|
a53375204e | ||
|
|
b1392361f8 | ||
|
|
7b4fbffae2 | ||
|
|
34e7b9f2ad | ||
|
|
f0080bdeae | ||
|
|
0eb27389da | ||
|
|
2c5eed9ee2 | ||
|
|
2c1a5bd032 | ||
|
|
298f8346e9 | ||
|
|
1897c5a4ba | ||
|
|
57e435271e | ||
|
|
7e9b430347 | ||
|
|
ca5b3e626b | ||
|
|
3a404f29fa | ||
|
|
16073d6872 | ||
|
|
dce563d2f5 | ||
|
|
8d556a5b84 | ||
|
|
a61063e5b8 | ||
|
|
94973867db | ||
|
|
214c2dcae8 | ||
|
|
72b36bf012 | ||
|
|
4335e6ceac | ||
|
|
b5f92a7d54 | ||
|
|
41ec75d264 | ||
|
|
6d6ad1f487 | ||
|
|
3ac33d21ac | ||
|
|
04e4b37f6f | ||
|
|
3e5903de6a | ||
|
|
04ea0fe524 | ||
|
|
955d6751a9 | ||
|
|
30c43bff10 | ||
|
|
e009244566 | ||
|
|
3d3cd2c2d8 | ||
|
|
f5498371ec | ||
|
|
c3b95bed8c | ||
|
|
8ce7d6c0f6 | ||
|
|
e875f429a9 | ||
|
|
b6beff0488 | ||
|
|
60c69ac3f0 | ||
|
|
1fb9320421 | ||
|
|
9a176f6667 | ||
|
|
96ea9a9e42 | ||
|
|
e39fca0c11 | ||
|
|
2ec035005d | ||
|
|
b734b3aef0 | ||
|
|
0f5635f42d | ||
|
|
8557075b7c | ||
|
|
bc0f0e7087 | ||
|
|
8ce5f9aea3 | ||
|
|
050f9d3a4e | ||
|
|
a81bf0deb4 | ||
|
|
2059324c27 | ||
|
|
a09a0a1bca | ||
|
|
83712bb9f5 | ||
|
|
728ae47b9a | ||
|
|
2a9b272a14 | ||
|
|
8662deac43 | ||
|
|
e42644bbd8 | ||
|
|
07d30b6272 | ||
|
|
2a4f8543cc | ||
|
|
186b293cce | ||
|
|
2bfe72f39d | ||
|
|
f99f955223 | ||
|
|
ec56e69a3c | ||
|
|
3942583b1d | ||
|
|
a10b15ba4b | ||
|
|
5003cbd7a8 | ||
|
|
481a137c23 | ||
|
|
c3f7f0938d | ||
|
|
b1925fa38d | ||
|
|
d9f8a7a46f | ||
|
|
846a072bf9 | ||
|
|
5dd7bbd8a7 | ||
|
|
e1773acf24 | ||
|
|
03a0f97669 | ||
|
|
917a3f41e8 | ||
|
|
3c8da1b299 | ||
|
|
c61c7edbd0 | ||
|
|
53402d9a1c | ||
|
|
de9278b388 | ||
|
|
4fef6200f8 | ||
|
|
81771b7bd7 | ||
|
|
2fee77c42c | ||
|
|
968ecdb31d | ||
|
|
af7b36a88b | ||
|
|
6ad58d38e2 | ||
|
|
681b4ce155 | ||
|
|
9d21ac1b16 | ||
|
|
2b3fcca7e8 | ||
|
|
af8e786ab5 | ||
|
|
c8df1b8f1f | ||
|
|
4f921ddf6f | ||
|
|
4f5839870b | ||
|
|
c0d7f51d6c | ||
|
|
a81d770360 | ||
|
|
f64d5eab50 | ||
|
|
d773397fe9 | ||
|
|
2e30995bfc | ||
|
|
17a2547f18 | ||
|
|
87a5cd66c8 | ||
|
|
9436ace64f | ||
|
|
fde00f6bd8 | ||
|
|
04a72a069a | ||
|
|
e2dcb5bc15 | ||
|
|
c7040a257c | ||
|
|
602dc00c65 | ||
|
|
0339691571 | ||
|
|
9e1f3ec131 | ||
|
|
b8589819dc | ||
|
|
a3e87f4c01 | ||
|
|
21ab5a602e | ||
|
|
5d97d7b4b2 | ||
|
|
d8d7d0b372 | ||
|
|
b8323d41fc | ||
|
|
d0b5314201 | ||
|
|
547e36e73f | ||
|
|
e593a772cb | ||
|
|
4da09529b6 | ||
|
|
de375992e8 | ||
|
|
0bc4a29881 | ||
|
|
9575c92713 | ||
|
|
cf277874eb | ||
|
|
746e060402 | ||
|
|
dd3a7c816e | ||
|
|
814bc3ab2c | ||
|
|
dbaf6761df | ||
|
|
580e45827d | ||
|
|
f3b8de9d1f | ||
|
|
fb1c728b12 | ||
|
|
6964ca0d18 | ||
|
|
6e9a2f55fd | ||
|
|
691fa61362 | ||
|
|
0c1eda0d08 | ||
|
|
767eac2fa6 | ||
|
|
6f651fa2d0 | ||
|
|
e3362c2e3d | ||
|
|
08b8b68f9a | ||
|
|
daf9ca9e7f | ||
|
|
d1024359c9 | ||
|
|
ed6070aff9 | ||
|
|
e4dbfa3534 | ||
|
|
ddd2b707c0 | ||
|
|
cd4f1077c2 | ||
|
|
b472d1cb9d | ||
|
|
922e2548f4 | ||
|
|
45caa7c120 | ||
|
|
670ae45d62 | ||
|
|
05bcf018d1 | ||
|
|
0af5d2e0bb | ||
|
|
eaf05fe9be | ||
|
|
e97b23f345 | ||
|
|
83a00ded3d | ||
|
|
78f81cc968 | ||
|
|
5d3347b4fe | ||
|
|
64d2ef8170 | ||
|
|
7c1e360b9a | ||
|
|
575d36dcde | ||
|
|
8dba8f7491 | ||
|
|
cc39e5b905 | ||
|
|
0be7e6018f | ||
|
|
7697e3f0c4 | ||
|
|
379800c49f | ||
|
|
79e2515807 | ||
|
|
342f5743e2 | ||
|
|
0e81870b85 | ||
|
|
dd7a8fd0c1 | ||
|
|
4277331ee2 | ||
|
|
53561a728f | ||
|
|
d0fd8c4fe4 | ||
|
|
398989510b | ||
|
|
f8e3ad5685 | ||
|
|
fbea7ef874 | ||
|
|
dc2c6f8a21 | ||
|
|
5ee08583b6 | ||
|
|
bfbd278e7c | ||
|
|
4c6e5903e3 | ||
|
|
a7cd5672c1 | ||
|
|
3373b728b7 | ||
|
|
22521b7159 | ||
|
|
e5fb14138e | ||
|
|
1b2242330c | ||
|
|
356958cc55 | ||
|
|
8f1da32001 | ||
|
|
686352a397 | ||
|
|
ef79c42ebc | ||
|
|
c8fc5378c1 | ||
|
|
c296666d8e | ||
|
|
f193e260b0 | ||
|
|
82981a9a54 | ||
|
|
3be54ca484 | ||
|
|
2f2c177674 | ||
|
|
1f47223918 | ||
|
|
eb646696a3 | ||
|
|
7cfe5160d5 | ||
|
|
95135c4379 | ||
|
|
7e604d6a5b | ||
|
|
64ac2666f9 | ||
|
|
05b3459342 | ||
|
|
92ad5f2407 | ||
|
|
e3c60e3202 | ||
|
|
7b5bcb05b1 | ||
|
|
154f94a0af | ||
|
|
063d3ee313 | ||
|
|
79859d05c0 | ||
|
|
acd3a94c46 | ||
|
|
13f09315e7 | ||
|
|
890528bf14 | ||
|
|
e4aafcf81e | ||
|
|
81c3c34ab8 | ||
|
|
b7b83b26b5 | ||
|
|
639cd3dfae | ||
|
|
7cf1302e8a | ||
|
|
dd5dd53a38 | ||
|
|
7275b8eac7 | ||
|
|
408c6fc998 | ||
|
|
5ce638572f | ||
|
|
4b98490ff9 | ||
|
|
6ea18ec75b | ||
|
|
56e2ffec5c | ||
|
|
fa204a208a | ||
|
|
9ab0fc593f | ||
|
|
3b9c454245 | ||
|
|
a6fc7a0da0 | ||
|
|
53ae57e478 | ||
|
|
1d3401e3b4 | ||
|
|
634198df06 | ||
|
|
cffc3953ea | ||
|
|
ea768602fb | ||
|
|
b4fc6dddd3 | ||
|
|
96d90c217e | ||
|
|
a2f1722455 | ||
|
|
400b51df1c | ||
|
|
0f3ce6917e | ||
|
|
11e57fe7ad | ||
|
|
2ddce8723d | ||
|
|
291668647c | ||
|
|
d3c41f2492 | ||
|
|
10fa3cb27d | ||
|
|
d8f95edff5 | ||
|
|
37ffe86d8b | ||
|
|
87fdbfdcc5 | ||
|
|
424a218860 | ||
|
|
faf0ae6bdc | ||
|
|
e46c42554b | ||
|
|
eb16440ba6 | ||
|
|
12f81353e0 | ||
|
|
fd33a8acd1 | ||
|
|
374e268a4f | ||
|
|
405bfbf9ba | ||
|
|
304227200f | ||
|
|
dc10125380 | ||
|
|
51c417ebc3 | ||
|
|
862230f58a | ||
|
|
6416fc56d7 | ||
|
|
a8ad8e5f5a | ||
|
|
d5edf29554 | ||
|
|
4351099e79 | ||
|
|
196d07edc6 | ||
|
|
f4bb03039a | ||
|
|
d6427f0fc8 | ||
|
|
f33a6d7634 | ||
|
|
2b931fb3f0 | ||
|
|
bb586892ba | ||
|
|
cb18f60f82 | ||
|
|
e5023943e5 | ||
|
|
c565dc5af7 | ||
|
|
5634903aa0 | ||
|
|
ce81a9cb22 | ||
|
|
5a01a1a30a | ||
|
|
cb704cb1e7 | ||
|
|
6e2dda7993 | ||
|
|
15e1d6d1a2 | ||
|
|
ba588b9eef | ||
|
|
f48b848eb6 | ||
|
|
f81fd74aa3 | ||
|
|
ad608b08e0 | ||
|
|
f9e80b709a | ||
|
|
f75b62e62c | ||
|
|
1c24a55d4b | ||
|
|
43dbb55d50 | ||
|
|
6ec974f996 | ||
|
|
03418299b8 | ||
|
|
f5bd86593c | ||
|
|
2af78eaab2 | ||
|
|
67cd003afe | ||
|
|
f7f11abfc2 | ||
|
|
52aa5f02e2 | ||
|
|
ce8175be61 | ||
|
|
0bc542f851 | ||
|
|
8e4f88ce5b | ||
|
|
d1c759f04f | ||
|
|
362ea83549 | ||
|
|
4cb7b999ad | ||
|
|
81482b7421 | ||
|
|
2b7807f300 | ||
|
|
ef23d022ee | ||
|
|
02d7fdc4f9 | ||
|
|
ccb3351607 | ||
|
|
72f9c6d81b | ||
|
|
bba70b4c46 | ||
|
|
46073e0a6c | ||
|
|
93a44f494d | ||
|
|
5c96f877ed | ||
|
|
23ea7e0511 | ||
|
|
137b3d7b5d | ||
|
|
13ffd92210 | ||
|
|
4725f8b3ca | ||
|
|
6d65a90de9 | ||
|
|
faf928527d | ||
|
|
18c6e80c3c | ||
|
|
83045c743a | ||
|
|
4940912784 | ||
|
|
a7fd2bd058 | ||
|
|
aa1f61a4f8 | ||
|
|
b103e817ed | ||
|
|
55045badce | ||
|
|
e951e23bc4 | ||
|
|
d467f159ad | ||
|
|
bb2a7b8d6c | ||
|
|
23bb8ec482 | ||
|
|
6c50fe1011 | ||
|
|
4268cb31c3 | ||
|
|
3b37d56427 | ||
|
|
f239075c26 | ||
|
|
b0c8c42c85 | ||
|
|
ea777b67ec | ||
|
|
ebf1486a7d | ||
|
|
2db2f55d16 | ||
|
|
cf9f34c0be | ||
|
|
4d4bec95f2 | ||
|
|
f3a5ce75d5 | ||
|
|
b38ce5e812 | ||
|
|
e4733fa02c | ||
|
|
39ea443f81 | ||
|
|
d03806aea2 | ||
|
|
576c281150 | ||
|
|
dfabcd691a | ||
|
|
e2698e71a3 | ||
|
|
6901628b5a | ||
|
|
fc3912ca7d | ||
|
|
c83cb4496d | ||
|
|
a76228c1e1 | ||
|
|
9447f2933a | ||
|
|
26d4664cc5 | ||
|
|
05fa9d887d | ||
|
|
acdad028a3 | ||
|
|
890ababe0a | ||
|
|
de78615038 | ||
|
|
db35670432 | ||
|
|
4d954b2ab0 | ||
|
|
1c215c36af | ||
|
|
2e8f64b20a | ||
|
|
9c764c90e3 | ||
|
|
95a4c19dc6 | ||
|
|
4d155a6b4f | ||
|
|
e3352f90e1 | ||
|
|
677a9da80a | ||
|
|
83e53c09eb | ||
|
|
c7e1e251ba | ||
|
|
aff7af5159 | ||
|
|
7bd77d666d | ||
|
|
9a7eb4b9a5 | ||
|
|
903b5f39df | ||
|
|
55f0ca3e9e | ||
|
|
3387e677ba | ||
|
|
5774acfc81 | ||
|
|
0eee2d1d0a | ||
|
|
0c624cc576 | ||
|
|
aade1008c4 | ||
|
|
c58b099230 | ||
|
|
786092bdaf | ||
|
|
80adf03926 | ||
|
|
4b9c35d53b | ||
|
|
f3623dccf6 | ||
|
|
b0db1e3d40 | ||
|
|
b936c3f857 | ||
|
|
600b9a6fb0 | ||
|
|
3bec2ef0b7 | ||
|
|
3d8344f23c | ||
|
|
d87836d0a9 | ||
|
|
42908ceb6f | ||
|
|
70288c94c3 | ||
|
|
2bc63c2ab6 | ||
|
|
609cbff2da | ||
|
|
3cf0931fb8 | ||
|
|
a42d2452fd | ||
|
|
7dd79874cc | ||
|
|
d1a75f076e | ||
|
|
08fa833f82 | ||
|
|
45e869e0d6 | ||
|
|
46cfc882c2 | ||
|
|
10583a4b9b | ||
|
|
da2adf3059 | ||
|
|
da24c9164a | ||
|
|
8ac41533b6 | ||
|
|
76958f285c | ||
|
|
93f6f3aecf | ||
|
|
971f0c06e7 | ||
|
|
bd4e0483d4 | ||
|
|
838eff3037 | ||
|
|
1ee1c11700 | ||
|
|
daa6db164a | ||
|
|
eb33542e4a | ||
|
|
a03b0c94c4 | ||
|
|
402aea1493 | ||
|
|
26c0baefe7 | ||
|
|
057d22adc1 | ||
|
|
77f3806abf | ||
|
|
5f6689adc1 | ||
|
|
051ec71263 | ||
|
|
c3434814c1 | ||
|
|
38325c5af4 | ||
|
|
589d0545cb | ||
|
|
32b74608bf | ||
|
|
98c0be147b | ||
|
|
6442e8c891 | ||
|
|
9454924b9f | ||
|
|
7233f00c32 | ||
|
|
905db42625 | ||
|
|
40e02899bb | ||
|
|
bfdf24afb4 | ||
|
|
588269f1a0 | ||
|
|
040b965be2 | ||
|
|
363951eb94 | ||
|
|
a45283b128 | ||
|
|
05d5ad47f2 | ||
|
|
5ccb858d7f | ||
|
|
c49c808730 | ||
|
|
23d44aef7e | ||
|
|
657beea858 | ||
|
|
4becfc6b88 | ||
|
|
b2763b1f4f | ||
|
|
cbd4fc1a80 | ||
|
|
6a17eb1e86 | ||
|
|
4e847b4293 | ||
|
|
e81d8ffa3c | ||
|
|
2e1de8a48f | ||
|
|
240971172d | ||
|
|
1b54f3b87c | ||
|
|
cf6ae51f76 | ||
|
|
12619f4f3b | ||
|
|
4b96ce4a54 | ||
|
|
86c1f57128 | ||
|
|
7a51b46178 | ||
|
|
eeb1d8bff7 | ||
|
|
04f757913a | ||
|
|
307b7be28d | ||
|
|
c3d188bde4 | ||
|
|
52c2b4e854 | ||
|
|
bd970eff83 | ||
|
|
7bba5fafe0 | ||
|
|
d25c69abbf | ||
|
|
22e1e8a82f | ||
|
|
c8c3ab76d6 | ||
|
|
ad1c39a7e5 | ||
|
|
e6398872cd | ||
|
|
811d27529c | ||
|
|
a1efafc871 | ||
|
|
7f2a4bdfd8 | ||
|
|
8381b2d348 | ||
|
|
a95c2ec42b | ||
|
|
ad91178ef7 | ||
|
|
db179d7b67 | ||
|
|
f6e2651f88 | ||
|
|
6a52945e5a | ||
|
|
f8a66b0f9b | ||
|
|
bfc3b0cc43 | ||
|
|
2432378a57 | ||
|
|
2f1ae9418a | ||
|
|
619eeb3f02 | ||
|
|
ed1862cf72 | ||
|
|
40d5b08f39 | ||
|
|
fcfccd18dc | ||
|
|
0be1acfe72 | ||
|
|
7407cbb4fb | ||
|
|
79158aa3e5 | ||
|
|
e7d212fb8c | ||
|
|
4d6f75a65a | ||
|
|
17445ee8b9 | ||
|
|
2de950cce7 | ||
|
|
326dd096fd | ||
|
|
416b5e691e | ||
|
|
f7d91f1b48 | ||
|
|
46f823eb89 | ||
|
|
e54bfec4c1 | ||
|
|
e95a09dbff | ||
|
|
e1f01f963f | ||
|
|
a397f681b3 | ||
|
|
0b52c498da | ||
|
|
5141d82e49 | ||
|
|
262c014cfe | ||
|
|
dcd4ac9703 | ||
|
|
566457308f | ||
|
|
c967dbeefc | ||
|
|
0bcef1be3b | ||
|
|
11037f1071 | ||
|
|
bec9fc9c80 | ||
|
|
7e1cf051e8 | ||
|
|
7ea8e89766 | ||
|
|
4fc20964b3 | ||
|
|
cbc14a8c83 | ||
|
|
355be63b6f | ||
|
|
29b431009c | ||
|
|
4114730d91 | ||
|
|
b1731531b1 | ||
|
|
d2a092d032 | ||
|
|
4ce8c63044 | ||
|
|
5b450be094 | ||
|
|
9c80f4d9cb | ||
|
|
3c615f180b | ||
|
|
13b37abcb4 | ||
|
|
46696d9eb3 | ||
|
|
9f450ef0cb | ||
|
|
1a15ee757b | ||
|
|
eab0d6650e | ||
|
|
2829c77cc8 | ||
|
|
25d3a9004c | ||
|
|
b0bdab3ef2 | ||
|
|
e69cf89fec | ||
|
|
45007a6aa4 | ||
|
|
40e8dd3575 | ||
|
|
be92c0a3e1 | ||
|
|
46a1a4ce04 | ||
|
|
4fadb413c3 | ||
|
|
99436f1b4d | ||
|
|
3f7a55c48e | ||
|
|
2fffd25e05 | ||
|
|
e5d02419f7 | ||
|
|
30919d7e9e | ||
|
|
96903ea77d | ||
|
|
f4572f6f53 | ||
|
|
55dbafb9b0 | ||
|
|
a6c19bc286 | ||
|
|
c7450adc77 | ||
|
|
f4f3adf576 | ||
|
|
0e9ccc955b | ||
|
|
54b502629f | ||
|
|
8a25d0d293 | ||
|
|
a4af46fcf9 | ||
|
|
e9d3b573b3 | ||
|
|
17f48a9bc1 | ||
|
|
32922c6263 | ||
|
|
57874f399b | ||
|
|
5abca6711e | ||
|
|
875b98415b | ||
|
|
d577b1a135 | ||
|
|
2cd52e43b0 | ||
|
|
69bdc358eb | ||
|
|
2b2034f2da | ||
|
|
fb114a17a3 | ||
|
|
ba2e4fe16e | ||
|
|
948681b82e | ||
|
|
5bd532dd57 | ||
|
|
aef74d6480 | ||
|
|
ef8565b67f | ||
|
|
7b38b5dc96 | ||
|
|
9f5d9fe36b | ||
|
|
643d0620d7 | ||
|
|
8ecc1839a0 | ||
|
|
31aeae8bd1 | ||
|
|
26bbcae0bd | ||
|
|
0feca50ebb | ||
|
|
895f330e14 | ||
|
|
30f454de08 | ||
|
|
8347fa7874 | ||
|
|
6b5d335519 | ||
|
|
44f0473a09 | ||
|
|
a6bae01476 | ||
|
|
c356246f82 | ||
|
|
a6d9badc5f | ||
|
|
5bf179810b | ||
|
|
fbb75d6dd1 | ||
|
|
ac9db6706c | ||
|
|
9b489f1e5c | ||
|
|
ab788eaaa2 | ||
|
|
826106090b | ||
|
|
e6e9a74766 | ||
|
|
5fee3efb35 | ||
|
|
a3d77a76aa | ||
|
|
3a958294f3 | ||
|
|
086a518a53 | ||
|
|
705fabb32b | ||
|
|
3d37a6ac2f | ||
|
|
dd79e348d3 | ||
|
|
a913d3eb32 | ||
|
|
f32049bdb3 | ||
|
|
bbcc7a502d | ||
|
|
2fb2ab02c4 | ||
|
|
1a94004de4 | ||
|
|
424f2cc403 | ||
|
|
bae960fd5b | ||
|
|
eab77a9e61 | ||
|
|
fc78d9143b | ||
|
|
034dbca30c | ||
|
|
a41adc6c9e | ||
|
|
bd170938c5 | ||
|
|
e91a73a32e | ||
|
|
099886e1bb | ||
|
|
c05dc8d7ae | ||
|
|
3cebfb3065 | ||
|
|
4e9f4a8010 | ||
|
|
94d99da821 | ||
|
|
ee1b596358 | ||
|
|
f7445d1777 | ||
|
|
ed5abd5791 | ||
|
|
898b847211 | ||
|
|
889dd15772 | ||
|
|
2ce6c1840b | ||
|
|
81f0cecb79 | ||
|
|
d46d77411b | ||
|
|
ee4f4d8af1 | ||
|
|
ea1426a24b | ||
|
|
120677a91f | ||
|
|
bd78e4c4de | ||
|
|
e3f70b6cd6 | ||
|
|
616712cf79 | ||
|
|
a5007df1bc | ||
|
|
d6720b67ed | ||
|
|
2261fd6adb | ||
|
|
9334ad6991 | ||
|
|
7b5e4143c3 | ||
|
|
e63e5502cd | ||
|
|
154794e774 | ||
|
|
4aa71725dd | ||
|
|
9bd2e7fea4 | ||
|
|
e6d3e7d7da | ||
|
|
35bb15b5df | ||
|
|
e54d61fd87 | ||
|
|
f28b2836c7 | ||
|
|
d196f1f327 | ||
|
|
995f90ca53 | ||
|
|
c1da380c9b | ||
|
|
77f77b8c7d | ||
|
|
b3c1aec461 | ||
|
|
ef242b52bb | ||
|
|
af1d5694dc | ||
|
|
a5ac47ff6d | ||
|
|
b03a4974c4 | ||
|
|
8385cd0bd7 | ||
|
|
ca67aa7f5f | ||
|
|
9b9ed514c8 | ||
|
|
11b7f6ab2f | ||
|
|
021f2074b8 | ||
|
|
c1ba2d4b3c | ||
|
|
141ad17ece | ||
|
|
74c81e2270 | ||
|
|
7bb124b6fe | ||
|
|
8a8ff10b19 | ||
|
|
1eef32dd8e | ||
|
|
d7b5dd416d | ||
|
|
536a94de45 | ||
|
|
d8ef471eb2 | ||
|
|
ff07a80078 | ||
|
|
310d31a3b1 | ||
|
|
8a1ef7da87 | ||
|
|
c142779ee8 | ||
|
|
54020d317e | ||
|
|
91d1ec6c2f | ||
|
|
1d3fd0dc9d | ||
|
|
8a7511cecb | ||
|
|
640f366c7e | ||
|
|
9f36c1d6de | ||
|
|
b3c8c078a8 | ||
|
|
0af0c01ec0 | ||
|
|
3ff2b0d6ff | ||
|
|
35b2b350a0 | ||
|
|
046ea1d79f | ||
|
|
3081508863 | ||
|
|
4a757c1bf1 | ||
|
|
4f1971a63d | ||
|
|
dec4bcca00 | ||
|
|
5443039b8c | ||
|
|
95e68f49f3 | ||
|
|
7e90956b50 | ||
|
|
0c84c8f1f3 | ||
|
|
56b3239e30 | ||
|
|
f8e85941da | ||
|
|
15081aa9c3 | ||
|
|
ac03a2bda3 | ||
|
|
b7ffa22f3a | ||
|
|
ac5e7069da | ||
|
|
5a83f38bca | ||
|
|
3abd59e290 | ||
|
|
d08fdf2e9e | ||
|
|
bad2f54e72 | ||
|
|
fc9b713851 | ||
|
|
245200840d | ||
|
|
3f87610e8c | ||
|
|
c285cb1bcc | ||
|
|
63968b564b | ||
|
|
e237c48186 | ||
|
|
622b121535 | ||
|
|
20774d4a40 | ||
|
|
7bb6bb85ec | ||
|
|
da908a84bc | ||
|
|
b515e259c0 | ||
|
|
facd551518 | ||
|
|
0fc569d9d9 | ||
|
|
da27a27ad5 | ||
|
|
5d4a20f622 | ||
|
|
70b15a373b | ||
|
|
01353f81b3 | ||
|
|
22f10b6581 | ||
|
|
785178ffb1 | ||
|
|
f9b5c58402 | ||
|
|
8ed6d63ce5 | ||
|
|
990a7c2052 | ||
|
|
09b0c09472 | ||
|
|
f83c38b58e | ||
|
|
51e600797a | ||
|
|
afb6ea1d9c | ||
|
|
39d6d1fd26 | ||
|
|
2dff63b101 | ||
|
|
b928892f0a | ||
|
|
c0188ea51d | ||
|
|
fa376ed5a4 | ||
|
|
6382edeb6e | ||
|
|
61d6c2dd1f | ||
|
|
44194f0b4e | ||
|
|
7103c7d32c | ||
|
|
b4e1663cd1 | ||
|
|
b3d16875d6 | ||
|
|
47412c89ca | ||
|
|
20d65f2ed3 | ||
|
|
46a559fb1d | ||
|
|
2769b22721 | ||
|
|
60ec6e8294 | ||
|
|
63520f9aff | ||
|
|
333b55a9f2 | ||
|
|
c6d3fd1a82 | ||
|
|
8106133ed0 | ||
|
|
b36111f63e | ||
|
|
3ad0284394 | ||
|
|
245ebf8c41 | ||
|
|
8309562da1 | ||
|
|
de807a65a6 | ||
|
|
92fe583421 | ||
|
|
b7ec05e88a | ||
|
|
203e925888 | ||
|
|
fde5453bf3 | ||
|
|
4c6a65565b | ||
|
|
e60ecfb8f5 | ||
|
|
b72e2610ca | ||
|
|
8d4bae06bc | ||
|
|
847b597d0f | ||
|
|
db1743f617 | ||
|
|
7ac1b8aacf | ||
|
|
55f8cb1f0e | ||
|
|
93574736cd | ||
|
|
e43f4b1a37 | ||
|
|
4ba33578ce | ||
|
|
ae00866005 | ||
|
|
21cb4dae29 | ||
|
|
cf086e6614 | ||
|
|
7d3ac98998 | ||
|
|
5e9d01aec2 | ||
|
|
a27d2d41f2 | ||
|
|
c09eabf347 | ||
|
|
38c2aed74a | ||
|
|
cf70671dba | ||
|
|
f90ce83a74 | ||
|
|
fab594ee32 | ||
|
|
d25cefe355 | ||
|
|
747eee1d29 | ||
|
|
0c43ee9ab8 | ||
|
|
466f3acd71 | ||
|
|
80add4ef12 | ||
|
|
959319c335 | ||
|
|
0e9ca547cb | ||
|
|
6e17e5ce7e | ||
|
|
858d7ac2ef | ||
|
|
4046321297 | ||
|
|
0cfdabd25a | ||
|
|
8b6cb6c5d8 | ||
|
|
3df3b7766c | ||
|
|
0d83654197 | ||
|
|
bd9e44382e | ||
|
|
2a7c20ea94 | ||
|
|
bde0dc9a17 | ||
|
|
7d7336ae01 | ||
|
|
e5e608324d | ||
|
|
569c1444f7 | ||
|
|
dc5ef28324 | ||
|
|
aea6c0eab8 | ||
|
|
c80a15d0cf | ||
|
|
1ddd57aa1d | ||
|
|
55adb0da6b | ||
|
|
a716289cc8 | ||
|
|
093d71fff4 | ||
|
|
4fe40e348d | ||
|
|
29a67b806d | ||
|
|
0169f42747 | ||
|
|
ed45b09241 | ||
|
|
107903cc99 | ||
|
|
92e100c497 | ||
|
|
536fe970f7 | ||
|
|
85526b06b6 | ||
|
|
d9b6c048d5 | ||
|
|
7e46a6529a | ||
|
|
2dc5fd80da | ||
|
|
e89cc8ca24 | ||
|
|
d39aeb0691 | ||
|
|
da9d98134a | ||
|
|
9992a9a0e4 | ||
|
|
adc8a16e85 | ||
|
|
13fb586ded | ||
|
|
2e63982f5a | ||
|
|
58b833c18a | ||
|
|
cb424eab00 | ||
|
|
9f2e18c3ee | ||
|
|
b44a73aea5 | ||
|
|
9c5759286f | ||
|
|
74dc714736 | ||
|
|
83751e22cc | ||
|
|
db5fdd75c4 | ||
|
|
4be2104d4b | ||
|
|
b6bab7618f | ||
|
|
3e1fda6f3b | ||
|
|
8487a031ee | ||
|
|
efbb123fce | ||
|
|
5a335d4f1c | ||
|
|
5770a823d6 | ||
|
|
52d7be9108 | ||
|
|
9512b9c6c4 | ||
|
|
da9ab642ec | ||
|
|
718ca1c7ab | ||
|
|
ee3742c5a0 | ||
|
|
7eef843a7a | ||
|
|
b4a8b06f07 | ||
|
|
4e13609985 | ||
|
|
2e5e4328f6 | ||
|
|
d98a11a8fa | ||
|
|
bdb25cbb66 | ||
|
|
369804cb6e | ||
|
|
1b08a92095 | ||
|
|
e787454d53 | ||
|
|
31d1ba663a | ||
|
|
c3731d8ff6 | ||
|
|
c5b46beb1a | ||
|
|
c5ca576c98 | ||
|
|
eae6458b42 | ||
|
|
aa1aa913b6 | ||
|
|
44084592cb | ||
|
|
6cacfb7b16 | ||
|
|
6372ce5647 | ||
|
|
306d3a7081 | ||
|
|
442530061f | ||
|
|
961a6f6ebc | ||
|
|
0d0c8e1b97 | ||
|
|
5b843ba2c4 | ||
|
|
8f9b46cdbe | ||
|
|
e16885a044 | ||
|
|
06a2fa05be | ||
|
|
d26f90b98e | ||
|
|
b47c128eb3 | ||
|
|
9d957b3c77 | ||
|
|
8ec5615569 | ||
|
|
fae73b827a | ||
|
|
6477437872 | ||
|
|
6099f46dea | ||
|
|
5009e6ef47 | ||
|
|
c4450d3259 | ||
|
|
0c3339f1c9 | ||
|
|
faee3d5ad6 | ||
|
|
a279963b28 | ||
|
|
f7c0c95d3b | ||
|
|
b8df07b547 | ||
|
|
d0e2730518 | ||
|
|
6dab82f01a | ||
|
|
7a01116db5 | ||
|
|
8f1e4ceff0 | ||
|
|
353a39d66a | ||
|
|
66196c0d56 | ||
|
|
2d59ba0943 | ||
|
|
33f92d1a5f | ||
|
|
4bd468f03e | ||
|
|
c6eaecd596 | ||
|
|
a2a5b06024 | ||
|
|
825732f60f | ||
|
|
596ec17106 | ||
|
|
fbd0f352c4 | ||
|
|
2600052735 | ||
|
|
a985b2ce09 | ||
|
|
829c176644 | ||
|
|
7d7d247bc2 | ||
|
|
43ae8e2a81 | ||
|
|
b0f37e9465 | ||
|
|
396ef55267 | ||
|
|
4b07469bb2 | ||
|
|
260f7b06c1 | ||
|
|
9733178228 | ||
|
|
67ba28a3cb | ||
|
|
a768d22a1d | ||
|
|
ede88550da | ||
|
|
ab55a0d134 | ||
|
|
bfd7060044 | ||
|
|
bf215a0f96 | ||
|
|
a2e1fb36df | ||
|
|
4e9c6f34b3 | ||
|
|
b08c0f2ec6 | ||
|
|
4c0e358afc | ||
|
|
9ae21b064a | ||
|
|
2df0c12e10 | ||
|
|
d37025dc6c | ||
|
|
0b01eb5ee4 | ||
|
|
d537c56159 | ||
|
|
feb9e3af10 | ||
|
|
ec30ed8439 | ||
|
|
cda9bb0e45 | ||
|
|
17f1c6b647 | ||
|
|
98079ec1ec | ||
|
|
16aaf9b5f8 | ||
|
|
ff0264ee15 | ||
|
|
bea9bd64a4 | ||
|
|
544a19906e | ||
|
|
208bb25118 | ||
|
|
fdb7e278c1 | ||
|
|
a132a49d57 | ||
|
|
23e73f5e88 | ||
|
|
fdcc5e9a66 | ||
|
|
77e7b1a2cb | ||
|
|
db95da3742 | ||
|
|
dc172a1476 | ||
|
|
8694a929cf | ||
|
|
36b3840362 | ||
|
|
d5fcbe842f | ||
|
|
155349dac0 | ||
|
|
7956a849d9 | ||
|
|
0d1c4cdc02 | ||
|
|
8c833a5df8 | ||
|
|
2c5bb977cb | ||
|
|
cddf7dd8f6 | ||
|
|
306c18147e | ||
|
|
84815eb97d | ||
|
|
890c13a91f | ||
|
|
3887ec8091 | ||
|
|
726b06bb70 | ||
|
|
c8e07c283e | ||
|
|
88a5128c03 | ||
|
|
01f2d3b04f | ||
|
|
fef85a4467 | ||
|
|
e0eadc1f2d | ||
|
|
a881b73e8d | ||
|
|
606f5cfb62 | ||
|
|
40737d545b | ||
|
|
990be3afe8 | ||
|
|
7020c2d025 | ||
|
|
a6497c1252 | ||
|
|
9d528a8075 | ||
|
|
5aec8b6f28 | ||
|
|
830ee27169 | ||
|
|
5f2e5c6f4e | ||
|
|
cf4317b5f6 | ||
|
|
2453aea6f3 | ||
|
|
e95b0f840a | ||
|
|
83680d1207 | ||
|
|
fd135e9e49 | ||
|
|
e47eb9cb4e | ||
|
|
d288fdc7f2 | ||
|
|
9de73dab29 | ||
|
|
f0afc20ec6 | ||
|
|
bf75059347 | ||
|
|
78835a58c4 | ||
|
|
fdccae9a1e | ||
|
|
6d97d42f67 | ||
|
|
46001e4761 | ||
|
|
37644e1f57 | ||
|
|
8a04934fbd | ||
|
|
31e1b3055f | ||
|
|
b4d712fcb1 | ||
|
|
7847a4593b | ||
|
|
b2036e64f1 | ||
|
|
fd0bbcccfe | ||
|
|
7caa47f949 | ||
|
|
06b171901d | ||
|
|
e685fe2b7d | ||
|
|
7177e77a8d | ||
|
|
4cda32771b | ||
|
|
f896b65a87 | ||
|
|
3fff1b750a | ||
|
|
2380317953 | ||
|
|
bd9ade4d15 | ||
|
|
659d3533ee | ||
|
|
37c242576e | ||
|
|
e9a22a23e7 | ||
|
|
ae3816c1e0 | ||
|
|
e4661a5ae2 | ||
|
|
539d1889fe | ||
|
|
2dd5f05f1a | ||
|
|
60c9b38de4 | ||
|
|
8b66b068ea | ||
|
|
1507bc3f04 | ||
|
|
1e0baba919 | ||
|
|
4c9f47b1e1 | ||
|
|
b66446b7eb | ||
|
|
f1726e21ae | ||
|
|
8d48f8ad86 | ||
|
|
8b280f272e | ||
|
|
b92d4256ad | ||
|
|
914a04a386 | ||
|
|
12f3dd7db6 | ||
|
|
427032ab94 | ||
|
|
b55aaaa34d | ||
|
|
7cde877452 | ||
|
|
e399012f73 | ||
|
|
fe1d2646bd | ||
|
|
ea98bfbe9a | ||
|
|
7bc3277634 | ||
|
|
22e94c5a29 | ||
|
|
aa8cf0ff15 | ||
|
|
a22f97bd13 | ||
|
|
3fd2d1629d | ||
|
|
cd04204a5c | ||
|
|
eee55376e7 | ||
|
|
d3c0972d70 | ||
|
|
e3f5fa8e35 | ||
|
|
03c540b68c | ||
|
|
7f2f53b06c | ||
|
|
8064826b53 | ||
|
|
8bdff31693 | ||
|
|
6f05b4137b | ||
|
|
4207f3d6d1 | ||
|
|
98dbda696d | ||
|
|
7a34c94542 | ||
|
|
789b93776d | ||
|
|
c93ee64630 | ||
|
|
f54c3ad85c | ||
|
|
a9fcd00723 | ||
|
|
fb7cc4284e | ||
|
|
e7a0755c25 | ||
|
|
d1e02dc298 | ||
|
|
69814039ca | ||
|
|
2ffb7fcdb4 | ||
|
|
d1695b7f10 | ||
|
|
5f0f9a9eae | ||
|
|
aa2dedb76f | ||
|
|
d630811386 | ||
|
|
407e35c9d8 | ||
|
|
bb7f38ce31 | ||
|
|
1ffb2d360a | ||
|
|
5cbadc02c5 | ||
|
|
9aa8d9edf0 | ||
|
|
db84380844 | ||
|
|
bbb0d2154f | ||
|
|
2a937ac7c0 | ||
|
|
a96652094e | ||
|
|
dbf3de57f6 | ||
|
|
c00bc0ebbb | ||
|
|
b08e5a2c32 | ||
|
|
09db5d94e1 | ||
|
|
033e8f6b44 | ||
|
|
bef40f0e6c | ||
|
|
aa2f69125f | ||
|
|
d30f3960a7 | ||
|
|
5f43da94ba | ||
|
|
2aa8a0c935 | ||
|
|
c02f8c6cb5 | ||
|
|
aa0be474e2 | ||
|
|
c0161c9b33 | ||
|
|
71404f2205 | ||
|
|
514da1e2db | ||
|
|
75dfceb5da | ||
|
|
1ae76b4377 | ||
|
|
b6f90cba8e | ||
|
|
62af441a1d | ||
|
|
228b8957d3 | ||
|
|
b4ce999ab3 | ||
|
|
cc06a414fe | ||
|
|
d3c37c4e5f | ||
|
|
3b448b62b1 | ||
|
|
6a3f5658b1 | ||
|
|
f65e791522 | ||
|
|
d91304f9ad | ||
|
|
61ce00108e | ||
|
|
a4eb773eee | ||
|
|
cfc69f5a0f | ||
|
|
a44823c3ed | ||
|
|
8a166e5ba5 | ||
|
|
9a7aeff870 | ||
|
|
cb3bdb9df2 | ||
|
|
0be8d57eaa | ||
|
|
79b9cbf1d6 | ||
|
|
500df8737e | ||
|
|
b8acbd1bee | ||
|
|
0bde8a65ba | ||
|
|
d2884b8936 | ||
|
|
e692359b47 | ||
|
|
473746eab0 | ||
|
|
050878cbd6 | ||
|
|
e100f18bb0 | ||
|
|
05c82fc166 | ||
|
|
839c3e261f | ||
|
|
95b579d191 | ||
|
|
8656715753 | ||
|
|
05b6394c5c | ||
|
|
72860deb0f | ||
|
|
d3bdbf31ac | ||
|
|
639c694c13 | ||
|
|
f34f6dc51e | ||
|
|
b93e7b9abf | ||
|
|
995f615b10 | ||
|
|
39b95eff4f | ||
|
|
392625b774 | ||
|
|
306b9d28ca | ||
|
|
6fe87bba20 | ||
|
|
c0d534072d | ||
|
|
009221aa98 | ||
|
|
46e5aff5f9 | ||
|
|
59498361e7 | ||
|
|
c652da130d | ||
|
|
83246a1802 | ||
|
|
f255df0198 | ||
|
|
9e524ffc34 | ||
|
|
004cc0c469 | ||
|
|
52b78a7e73 | ||
|
|
bd089d76af | ||
|
|
d5025b54bf | ||
|
|
740497047d | ||
|
|
3f6cbd57b2 | ||
|
|
2c9524ed45 | ||
|
|
384922680a | ||
|
|
d2e9f8f4f8 | ||
|
|
b4f10f854e | ||
|
|
8ce64d2a7f | ||
|
|
d917e21364 | ||
|
|
32cedaf565 | ||
|
|
4c2a5e9a11 | ||
|
|
a41d2a46ff | ||
|
|
4794cbfb36 | ||
|
|
d021217cf7 | ||
|
|
4573d83831 | ||
|
|
2bb612ca3f | ||
|
|
35534112c6 | ||
|
|
f51e531f3a | ||
|
|
2490856ccb | ||
|
|
9a5a87b027 | ||
|
|
45b8c89865 | ||
|
|
e68e6dcd3d | ||
|
|
670ff4a15d | ||
|
|
b616a37800 | ||
|
|
ce488a3645 | ||
|
|
fb47a9c742 | ||
|
|
80ace81a12 | ||
|
|
1efdae5197 | ||
|
|
a4c88edfca | ||
|
|
8f38c2f627 | ||
|
|
bbf68d4ce8 | ||
|
|
e1eec47a22 | ||
|
|
fc05075817 | ||
|
|
5bb64b634a | ||
|
|
7bc2c2be13 | ||
|
|
27e2c044da | ||
|
|
1213e8d6ac | ||
|
|
3f58d68d2a | ||
|
|
803e62020e | ||
|
|
fde437312f | ||
|
|
18425c915b | ||
|
|
0de6892ddd | ||
|
|
dfb92ffec3 | ||
|
|
b7842f98f0 | ||
|
|
85317f1ee1 | ||
|
|
f22f60508f | ||
|
|
716bdaaf38 | ||
|
|
1b0e2b87de | ||
|
|
2c57b809d2 | ||
|
|
d9c96db212 | ||
|
|
5f7391a76b | ||
|
|
accd80eda8 | ||
|
|
e49499f085 | ||
|
|
521f8930d7 | ||
|
|
11b9a8eb6e | ||
|
|
0d4350ae24 | ||
|
|
62a6a25aa1 | ||
|
|
14a74e7312 | ||
|
|
3fad2f3430 | ||
|
|
c35d1e8791 | ||
|
|
0367255a2a | ||
|
|
f5f5552ecd | ||
|
|
046a22bd2b | ||
|
|
ad94ac7595 | ||
|
|
cfa3993b79 | ||
|
|
972793b98a | ||
|
|
35682bf5b8 | ||
|
|
b023f592aa | ||
|
|
a1c34646f1 | ||
|
|
9ac3768f1d | ||
|
|
ff7881130f | ||
|
|
37effda7c5 | ||
|
|
0cac7cb1a5 | ||
|
|
8d41d11ca3 | ||
|
|
0ef516d147 | ||
|
|
f57a30898c | ||
|
|
a10c67555d | ||
|
|
14d0df3926 | ||
|
|
c085aeaa68 | ||
|
|
8543afccca | ||
|
|
61b5603a3b | ||
|
|
e3efffb2ec | ||
|
|
fe9a342b42 | ||
|
|
c7668b4436 | ||
|
|
ccdf6b227f | ||
|
|
0aea384f41 | ||
|
|
467059cd26 | ||
|
|
f41af36ea9 | ||
|
|
e2f8902222 | ||
|
|
52bfd4cadc | ||
|
|
7cdc556292 | ||
|
|
039bda9eaf | ||
|
|
a6d73d6f8b | ||
|
|
8e5af59153 | ||
|
|
278467518e | ||
|
|
a7080a5778 | ||
|
|
6a71ef6745 | ||
|
|
10eb576260 | ||
|
|
f14acb79bf | ||
|
|
b8e011bd27 | ||
|
|
f6295308cd | ||
|
|
f981675850 | ||
|
|
93bb7610e6 | ||
|
|
23975ee359 | ||
|
|
14eaedf375 | ||
|
|
ced0b741b9 | ||
|
|
13e805b213 | ||
|
|
c424c1e394 | ||
|
|
77d68bdc73 | ||
|
|
a1555bb9cd | ||
|
|
3ca61b218e | ||
|
|
e7917277e7 | ||
|
|
aa18be17fa | ||
|
|
39c7af5f8d | ||
|
|
a5f7f8bbe4 | ||
|
|
420e491963 | ||
|
|
36f2ff997a | ||
|
|
c33807d052 | ||
|
|
fb3946b64f | ||
|
|
51322e7270 | ||
|
|
3f084d8525 | ||
|
|
b1f4002036 | ||
|
|
bb1cbe0902 | ||
|
|
a095634755 | ||
|
|
1b9ff074af | ||
|
|
f8361446a4 | ||
|
|
5713490f14 | ||
|
|
1ceac2a0a0 | ||
|
|
8a2967a0db | ||
|
|
86297720d5 | ||
|
|
1aeb2b96e2 | ||
|
|
4ee8b9d7f6 | ||
|
|
1d208ed5ec | ||
|
|
3883aaabab | ||
|
|
6fb3c070d0 | ||
|
|
d8d8b4ed73 | ||
|
|
907f46769f | ||
|
|
1ffdb717f7 | ||
|
|
9080603bce | ||
|
|
5796ae9084 | ||
|
|
50636e3a7e | ||
|
|
501d4c9dfc | ||
|
|
84cbc4ae04 | ||
|
|
cbb2a3e46f | ||
|
|
493197c073 | ||
|
|
31a2952101 | ||
|
|
acaccc23e8 | ||
|
|
70e339164d | ||
|
|
0de5d72d75 | ||
|
|
d604cc7faf | ||
|
|
d843a3e359 | ||
|
|
37586662b3 | ||
|
|
193687418f | ||
|
|
72e6bb9537 | ||
|
|
d69e790c61 | ||
|
|
01d41520d4 | ||
|
|
aea9eb9e01 | ||
|
|
26717b13e9 | ||
|
|
5f36417bd9 | ||
|
|
021ea34814 | ||
|
|
4a08fbdf28 | ||
|
|
268753091d | ||
|
|
ec688829b5 | ||
|
|
ec5bf58b0f | ||
|
|
f877d821f0 | ||
|
|
6c22cfef1e | ||
|
|
05305d858b | ||
|
|
e094237bbf | ||
|
|
77eb52bc51 | ||
|
|
c79834cec7 | ||
|
|
aefc5fded7 | ||
|
|
5fd5a5d4fa | ||
|
|
0368ecf7f3 | ||
|
|
d9ec5dcb56 | ||
|
|
030bc6c6b6 | ||
|
|
c1dd2fe0f4 | ||
|
|
4e0851868e | ||
|
|
276178c27c | ||
|
|
3006e6bcbf | ||
|
|
3a50c5686e | ||
|
|
f8eea4d082 | ||
|
|
8a42d77990 | ||
|
|
3980d1a9b0 | ||
|
|
53741ec26e | ||
|
|
c398cf46c9 | ||
|
|
e869ce4a64 | ||
|
|
4064be6577 | ||
|
|
1f00cf4151 | ||
|
|
bae0ca62b8 | ||
|
|
b7a51a2495 | ||
|
|
4f6a3e39d0 | ||
|
|
528f6b7402 | ||
|
|
c252f29e6d | ||
|
|
fea84c9652 | ||
|
|
9b9940f708 | ||
|
|
a34ab17307 | ||
|
|
477a3e7263 | ||
|
|
f94c9496df | ||
|
|
1c31281b7b | ||
|
|
0e5204ecb4 | ||
|
|
f3dc6235d7 | ||
|
|
37cdf1a19e | ||
|
|
1fb642c777 | ||
|
|
8f791ceb12 | ||
|
|
f40eaa0f56 | ||
|
|
cb34d17ba1 | ||
|
|
328ba82007 | ||
|
|
010ed1b047 | ||
|
|
5a81a77d92 | ||
|
|
c7ea10d206 | ||
|
|
a37d00b40a | ||
|
|
0168b768d2 | ||
|
|
9a85b57ba4 | ||
|
|
eafece6497 | ||
|
|
8f08271664 | ||
|
|
da0271e624 | ||
|
|
94f52fb4ac | ||
|
|
524c2922a4 | ||
|
|
0891d64654 | ||
|
|
d1c23f7442 | ||
|
|
8cbbe35f24 | ||
|
|
a21e9d706e | ||
|
|
57160c4d04 | ||
|
|
8b46a49e23 | ||
|
|
c11ebb49f7 | ||
|
|
e4c3935a1b | ||
|
|
ade062fdd3 | ||
|
|
b0f6357482 | ||
|
|
38a9c11286 | ||
|
|
0d95f02e60 | ||
|
|
1c30528eea | ||
|
|
d1b116d314 | ||
|
|
9d20fd41a8 | ||
|
|
54648bb973 | ||
|
|
fc4edb12f9 | ||
|
|
9a1b8d7ce2 | ||
|
|
6909975503 | ||
|
|
5d94bd990a | ||
|
|
1b2514e3ec | ||
|
|
0da4f40b48 | ||
|
|
67c8719f34 | ||
|
|
d5b60c6ac8 | ||
|
|
a99d2e9e26 | ||
|
|
5c7d89cb9e | ||
|
|
ae7810f0d3 | ||
|
|
5a90dc46f0 | ||
|
|
294f886588 | ||
|
|
17aec665cf | ||
|
|
959b25e8b7 | ||
|
|
9fd2bf3480 | ||
|
|
7b061a4e51 | ||
|
|
4fcd89390b | ||
|
|
667ffe9cd3 | ||
|
|
6f4086cd8c | ||
|
|
2a45a1a400 | ||
|
|
eee201de1e | ||
|
|
6be24bd22a | ||
|
|
ca927dec30 | ||
|
|
3a78ef46a3 | ||
|
|
bdb1cd0905 | ||
|
|
ffb556a637 | ||
|
|
40acfb5e9d | ||
|
|
de8bcfa0d2 | ||
|
|
9439f407da | ||
|
|
5095e62961 | ||
|
|
3301907864 | ||
|
|
151175c40f | ||
|
|
234d4fa537 | ||
|
|
f384e8a6e3 | ||
|
|
66068757e1 | ||
|
|
8a7cda5dd1 | ||
|
|
8e67104ba4 | ||
|
|
0c9da9ddc8 | ||
|
|
a5ef6aa126 | ||
|
|
c133b7a2c2 | ||
|
|
a0ca68cc41 | ||
|
|
41cae0bc93 | ||
|
|
b4198fde8c | ||
|
|
bd24f35738 | ||
|
|
6fcbb757b5 | ||
|
|
3b8825e5d2 | ||
|
|
5cf3244918 | ||
|
|
934c9ccc8b | ||
|
|
41dfdfd1e8 | ||
|
|
427fb59c99 | ||
|
|
ae825800f6 | ||
|
|
d72700acf6 | ||
|
|
3310a6a26f | ||
|
|
740b5aa772 | ||
|
|
04b55e764a | ||
|
|
beb4062bb1 | ||
|
|
5d4cd4acdc | ||
|
|
aec8198131 | ||
|
|
0a850e47df | ||
|
|
5544820c5e | ||
|
|
4f466d517a | ||
|
|
cd0f20ca2f | ||
|
|
b3661848dc | ||
|
|
548201c256 | ||
|
|
a54e5d9f8b | ||
|
|
536257afa1 | ||
|
|
5a71c3270a | ||
|
|
d194dd173f | ||
|
|
be03a9e984 | ||
|
|
a90177e7c0 | ||
|
|
be9e8ca47d | ||
|
|
eb9fe85c75 | ||
|
|
47183c405f | ||
|
|
2725923b9b | ||
|
|
f6c03ed7a2 | ||
|
|
76b5548216 | ||
|
|
cc57a34a32 | ||
|
|
f7099b62e6 | ||
|
|
093ee8916e | ||
|
|
ac0259157b | ||
|
|
9cb937798f | ||
|
|
11d4926c85 | ||
|
|
836211ae2b | ||
|
|
fddf3d3f58 | ||
|
|
b036d1079e | ||
|
|
137c39e918 | ||
|
|
8778d022cf | ||
|
|
043bdbacec | ||
|
|
e0e19b0258 | ||
|
|
c72e0f790a | ||
|
|
87e79110a2 | ||
|
|
faeae1af60 | ||
|
|
b371fbad01 | ||
|
|
90831e153d | ||
|
|
009d8275c1 | ||
|
|
05c88e0ffc | ||
|
|
7d12552932 | ||
|
|
b761505bb1 | ||
|
|
63367f4f31 | ||
|
|
6f9d6b4af3 | ||
|
|
6ed8287b01 | ||
|
|
d948e20682 | ||
|
|
42929dac58 | ||
|
|
74449c64a2 | ||
|
|
0bd164c69e | ||
|
|
d44c082134 | ||
|
|
d756b9bfe4 | ||
|
|
6144050212 | ||
|
|
b5fe456b0d | ||
|
|
37791ff391 | ||
|
|
c2d99163a6 | ||
|
|
d948353b99 | ||
|
|
2649cb75f6 | ||
|
|
7c8da4a4b9 | ||
|
|
b72f5d75f7 | ||
|
|
947826a764 | ||
|
|
906c69a86d | ||
|
|
9c65aadcc7 | ||
|
|
70a9a7bbbd | ||
|
|
09879e00ba | ||
|
|
5cbb7d940d | ||
|
|
050976d7a3 | ||
|
|
6a96d9f8b5 | ||
|
|
8c55dfbcf6 | ||
|
|
78c5b49b5a | ||
|
|
c33ca04a4f | ||
|
|
f12e66d315 | ||
|
|
8e4bb36df8 | ||
|
|
034412f6fe | ||
|
|
88d83ba72d | ||
|
|
829e7a33aa | ||
|
|
1dcde1538e | ||
|
|
b876cd0975 | ||
|
|
a06d11dc17 | ||
|
|
b1f5cd45c4 | ||
|
|
2c44c0e1f0 | ||
|
|
37c429b264 | ||
|
|
82994ce754 | ||
|
|
621f64c363 | ||
|
|
5591bf09d9 | ||
|
|
da94651656 | ||
|
|
419a77f144 | ||
|
|
f043358a59 | ||
|
|
24b17a8c27 | ||
|
|
26a64b788b | ||
|
|
89a05d247b | ||
|
|
718d549bb3 | ||
|
|
54a6a8324a | ||
|
|
7faf24cf88 | ||
|
|
7bebc7a814 | ||
|
|
8647a087dd | ||
|
|
78670665c4 | ||
|
|
1dd587cd83 | ||
|
|
d69cccf821 | ||
|
|
8ae3b9c28f | ||
|
|
9b6ad102b1 | ||
|
|
e6787b77fb | ||
|
|
46449045a6 | ||
|
|
d81984b4c6 | ||
|
|
22b463f306 | ||
|
|
475e45b848 | ||
|
|
32fac97d21 | ||
|
|
f9f32a1062 | ||
|
|
c90c3bbd05 | ||
|
|
ea42d9a061 | ||
|
|
cbae9a087b | ||
|
|
4da199c43e | ||
|
|
00d8660a91 | ||
|
|
76c2f6afe0 | ||
|
|
efa53bd83c | ||
|
|
01f6a1e1c0 | ||
|
|
1d1344ebc1 | ||
|
|
9f78703dee | ||
|
|
1a0e96338e | ||
|
|
eff4690e0e | ||
|
|
266480c234 | ||
|
|
a922d01005 | ||
|
|
b053b84197 | ||
|
|
0d9711c8bb | ||
|
|
edab68f4fb | ||
|
|
08f04e19ef | ||
|
|
e62234a6ac | ||
|
|
5499c7a96f | ||
|
|
7f9c5c25ae | ||
|
|
b1276d56f7 | ||
|
|
b53bf320a6 | ||
|
|
81a4c168ed | ||
|
|
512a1a806e | ||
|
|
c95ef05177 | ||
|
|
563bd8a6a3 | ||
|
|
b444542f4d | ||
|
|
6eded41eee | ||
|
|
de91ce182d | ||
|
|
afc7f85460 | ||
|
|
c1b4d7de39 | ||
|
|
cde5b83bca | ||
|
|
f00106a502 | ||
|
|
3ae2742717 | ||
|
|
8deac19945 | ||
|
|
2ad469a5f4 | ||
|
|
c67b111c77 | ||
|
|
ee770e7429 | ||
|
|
197a3adf6a | ||
|
|
269d39497b | ||
|
|
0c3a7ac02b | ||
|
|
07443548c9 | ||
|
|
e561f78ada | ||
|
|
bb88272251 | ||
|
|
ce2f3dafae | ||
|
|
488c67056c | ||
|
|
351e51208d | ||
|
|
0c79df7299 | ||
|
|
b9a5c5af35 | ||
|
|
59741b84d1 | ||
|
|
25efcdf750 | ||
|
|
49dfa6c2a6 | ||
|
|
659647353e | ||
|
|
0eba51c80b | ||
|
|
35996d11f4 | ||
|
|
e0e0a811eb | ||
|
|
76f400d0aa | ||
|
|
8e950e0f54 | ||
|
|
0adb9dd540 | ||
|
|
5825555534 | ||
|
|
306a8c6201 | ||
|
|
123b620085 | ||
|
|
831e7814be | ||
|
|
7c85199ac2 | ||
|
|
efec8e4f2f | ||
|
|
af4faef9cf | ||
|
|
90052ad9e3 | ||
|
|
35c7b16e4a | ||
|
|
7ac75acfc2 | ||
|
|
22662fddcd | ||
|
|
6df1cdf5d8 | ||
|
|
2287c51d73 | ||
|
|
372942a14f | ||
|
|
6362246da4 | ||
|
|
9986d69215 | ||
|
|
21644e5cba | ||
|
|
ad93217bf6 | ||
|
|
ca49bc1ddd | ||
|
|
1229c73ddc | ||
|
|
7416202555 | ||
|
|
a0ba683eea | ||
|
|
89654eb26f | ||
|
|
9d1736a141 | ||
|
|
eaa4ed3da5 | ||
|
|
0db3f65312 | ||
|
|
1ea0a3ccc5 | ||
|
|
16cd30bea8 | ||
|
|
075ba4c603 | ||
|
|
2d898822df | ||
|
|
25b8ec82e8 | ||
|
|
44b74e2681 | ||
|
|
485e171008 | ||
|
|
c12eb83b4b | ||
|
|
84060e7823 | ||
|
|
d80d50b59d | ||
|
|
f11f054fea | ||
|
|
4c1d491d5a | ||
|
|
2b67cc520c | ||
|
|
2a5712bd3c | ||
|
|
ccbc11408b | ||
|
|
22bae315be | ||
|
|
740ab7cb46 | ||
|
|
cdaa9aa1b0 | ||
|
|
875f82dcbb | ||
|
|
e8f6bdd64a | ||
|
|
25247491ee | ||
|
|
57ae3dc3a7 | ||
|
|
27d00b58d7 | ||
|
|
263821ce67 | ||
|
|
3c12247b00 | ||
|
|
56d41596f6 | ||
|
|
f0cd1965b4 | ||
|
|
86b6a1d88a | ||
|
|
f903e13d7b | ||
|
|
015206a760 | ||
|
|
0aff119260 | ||
|
|
ddb8608501 | ||
|
|
0d75a273f0 | ||
|
|
4f07d23dd6 | ||
|
|
79baa0d66e | ||
|
|
d5ca49ef9b | ||
|
|
536d7fb3c5 | ||
|
|
f66fd1f38c | ||
|
|
3a4c06a818 | ||
|
|
fd8dd7ab8a | ||
|
|
0434f6a935 | ||
|
|
b51a26442c | ||
|
|
6462ac0f0e | ||
|
|
fc01dbbac9 | ||
|
|
df8576c066 | ||
|
|
ca8adf28cf | ||
|
|
f70c6c566e | ||
|
|
056d4411b7 | ||
|
|
602b83b0e5 | ||
|
|
1cfcc6d930 | ||
|
|
8c6f618743 | ||
|
|
6adf1c3162 | ||
|
|
9f5f4f1832 | ||
|
|
9f49cc83e9 | ||
|
|
67972199ce | ||
|
|
9d5db86bf3 | ||
|
|
39efed5fc1 | ||
|
|
21c2bf22dd | ||
|
|
6c94b3a423 | ||
|
|
683248db0b | ||
|
|
9032400528 | ||
|
|
1a925b1acf | ||
|
|
dadc8c2c60 | ||
|
|
01c1b44bfc | ||
|
|
a394a99d8f | ||
|
|
4213707b7f | ||
|
|
d53b8272ee | ||
|
|
fdd688ac68 | ||
|
|
5bb961bdc6 | ||
|
|
9e7cc06f97 | ||
|
|
1d184d9000 | ||
|
|
a5e2ebf647 | ||
|
|
29eb573de5 | ||
|
|
ec6c3da5ec | ||
|
|
e2d4f8961e | ||
|
|
a48c680201 | ||
|
|
c869f2c962 | ||
|
|
f77fc9a06d | ||
|
|
e12eae93b9 | ||
|
|
d92fb32574 | ||
|
|
541dba3d79 | ||
|
|
033ed17125 | ||
|
|
aaeb663d15 | ||
|
|
c337005985 | ||
|
|
192eeee348 | ||
|
|
27c97684b9 | ||
|
|
41d5fa70ed | ||
|
|
4206e9c175 | ||
|
|
8658bb05dd | ||
|
|
9b707016a9 | ||
|
|
4b02826883 | ||
|
|
b29774ea71 | ||
|
|
bf68e90a8e | ||
|
|
cc5cdcd831 | ||
|
|
07f23ff7d9 | ||
|
|
2985da6dc9 | ||
|
|
1523973749 | ||
|
|
ccafd78a14 | ||
|
|
4f71fe0d55 | ||
|
|
7bd6b6b4d1 | ||
|
|
2b976489a2 | ||
|
|
1440f20f95 | ||
|
|
941e7e27c0 | ||
|
|
5428c6ab2f | ||
|
|
851bb65d17 | ||
|
|
a3ce04b7e8 | ||
|
|
4d68ca6aa2 | ||
|
|
29c6767d3c | ||
|
|
d8f5f7975c | ||
|
|
4cd8476837 | ||
|
|
112257449f | ||
|
|
ff9cf4adf0 | ||
|
|
f2d387bc9c | ||
|
|
2ceb5150e2 | ||
|
|
00006ec721 | ||
|
|
00aa6948ab | ||
|
|
bd24ed3af7 | ||
|
|
9fc455bcec | ||
|
|
0f8ba1e7e8 | ||
|
|
775dd037d6 | ||
|
|
49cbfe130c | ||
|
|
9cd61dd996 | ||
|
|
292c4aa060 | ||
|
|
56a265930d | ||
|
|
4171d110a4 | ||
|
|
d0775565e9 | ||
|
|
ca4d4a096c | ||
|
|
ff27db6b83 | ||
|
|
c9ecb6c563 | ||
|
|
b9e5782264 | ||
|
|
d852f81cb0 | ||
|
|
6137aa5d8e | ||
|
|
131b67ee83 | ||
|
|
db6f00be08 | ||
|
|
2ecc80985a | ||
|
|
93dbfd5110 | ||
|
|
ae0c384c85 | ||
|
|
08a81696a1 | ||
|
|
8375a8ae63 | ||
|
|
31d8cf5118 | ||
|
|
597b967e55 | ||
|
|
3f84ee3fcc | ||
|
|
38103ac90b | ||
|
|
13d27697e1 | ||
|
|
942f356d19 | ||
|
|
b87b687e2f | ||
|
|
2e313719bb | ||
|
|
0c5eb48fdb | ||
|
|
2ae2c81e0b | ||
|
|
222b154505 | ||
|
|
67c2de74f1 | ||
|
|
4a9b36807a | ||
|
|
c6241fab38 | ||
|
|
afbc69c6d2 | ||
|
|
2779cb4e25 | ||
|
|
f46ee93539 | ||
|
|
3eb087e5c1 | ||
|
|
59c935e723 | ||
|
|
bae45d277f | ||
|
|
0b6dfa9cd0 | ||
|
|
1ff3a6c92c | ||
|
|
f75cee0d78 | ||
|
|
229f16cb01 | ||
|
|
2c6b1a440f | ||
|
|
37afc1352f | ||
|
|
9943119033 | ||
|
|
41457ff551 | ||
|
|
82b64b5828 | ||
|
|
229e8acc74 | ||
|
|
30324e1c01 | ||
|
|
8ca356eae7 | ||
|
|
29f4ae368d | ||
|
|
409080f51b | ||
|
|
0b24c46279 | ||
|
|
49596c5ac1 | ||
|
|
9bf79db8f8 |
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Keep CRLF newlines in appropriate test files to have reproducible tests
|
||||||
|
core/pkg/fixhandler/testdata/inserts/*-crlf-newlines.yaml text eol=crlf
|
||||||
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: 'bug'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Description
|
||||||
|
<!-- A clear and concise description of what the bug is. -->
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
OS: ` ` <!-- the OS + version you’re running Kubescape on, e.g Ubuntu 22.04 LTS -->
|
||||||
|
Version: ` ` <!-- the version that Kubescape reports when you run `kubescape version` -->
|
||||||
|
|
||||||
|
# Steps To Reproduce
|
||||||
|
<!--
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Expected behavior
|
||||||
|
<!-- A clear and concise description of what you expected to happen. -->
|
||||||
|
|
||||||
|
# Actual Behavior
|
||||||
|
<!-- A clear and concise description of what happened. If applicable, add screenshots to help explain your problem. -->
|
||||||
|
|
||||||
|
# Additional context
|
||||||
|
<!-- Add any other context about the problem here. -->
|
||||||
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: 'feature'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
<!-- A brief overview of the related current state -->
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||||
|
|
||||||
|
## Solution
|
||||||
|
<!-- A clear and concise description of what you want to happen. -->
|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||||
|
|
||||||
|
## Additional context
|
||||||
|
<!-- Add any other context or screenshots about the feature request here. -->
|
||||||
|
|
||||||
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
47
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
## Overview
|
||||||
|
<!-- Please provide a brief overview of the changes made in this pull request. e.g. current behavior/future behavior -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Additional Information
|
||||||
|
|
||||||
|
> Any additional information that may be useful for reviewers to know
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## How to Test
|
||||||
|
|
||||||
|
> Please provide instructions on how to test the changes made in this pull request
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Examples/Screenshots
|
||||||
|
|
||||||
|
> Here you add related screenshots
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Related issues/PRs:
|
||||||
|
|
||||||
|
Here you add related issues and PRs.
|
||||||
|
If this resolved an issue, write "Resolved #<issue number>
|
||||||
|
|
||||||
|
e.g. If this PR resolves issues 1 and 2, it should look as follows:
|
||||||
|
* Resolved #1
|
||||||
|
* Resolved #2
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Checklist before requesting a review
|
||||||
|
|
||||||
|
put an [x] in the box to get it checked
|
||||||
|
|
||||||
|
- [ ] My code follows the style guidelines of this project
|
||||||
|
- [ ] I have commented on my code, particularly in hard-to-understand areas
|
||||||
|
- [ ] I have performed a self-review of my code
|
||||||
|
- [ ] If it is a core feature, I have added thorough tests.
|
||||||
|
- [ ] New and existing unit tests pass locally with my changes
|
||||||
|
|
||||||
|
**Please open the PR against the `dev` branch (Unless the PR contains only documentation changes)**
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
64
.github/workflows/01-golang-lint.yaml
vendored
Normal file
64
.github/workflows/01-golang-lint.yaml
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
name: golangci-lint
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
pull_request:
|
||||||
|
types: [ edited, opened, synchronize, reopened ]
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
- 'dev'
|
||||||
|
paths-ignore:
|
||||||
|
- '**.yaml'
|
||||||
|
- '**.md'
|
||||||
|
- '**.sh'
|
||||||
|
- 'website/*'
|
||||||
|
- 'examples/*'
|
||||||
|
- 'docs/*'
|
||||||
|
- 'build/*'
|
||||||
|
- '.github/*'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
# Optional: allow read access to pull request. Use with `only-new-issues` option.
|
||||||
|
pull-requests: read
|
||||||
|
jobs:
|
||||||
|
golangci:
|
||||||
|
name: lint
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- name: Install libgit2
|
||||||
|
run: make libgit2
|
||||||
|
- name: golangci-lint
|
||||||
|
continue-on-error: true
|
||||||
|
uses: golangci/golangci-lint-action@v3
|
||||||
|
with:
|
||||||
|
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
# Optional: working directory, useful for monorepos
|
||||||
|
# working-directory: somedir
|
||||||
|
|
||||||
|
# Optional: golangci-lint command line arguments.
|
||||||
|
# args: --issues-exit-code=0
|
||||||
|
args: --timeout 10m --build-tags=static
|
||||||
|
#--new-from-rev dev
|
||||||
|
|
||||||
|
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||||
|
only-new-issues: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the all caching functionality will be complete disabled,
|
||||||
|
# takes precedence over all other caching options.
|
||||||
|
# skip-cache: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
|
||||||
|
# skip-pkg-cache: true
|
||||||
|
|
||||||
|
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
|
||||||
|
# skip-build-cache: true
|
||||||
89
.github/workflows/build-image.yaml
vendored
Normal file
89
.github/workflows/build-image.yaml
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
client:
|
||||||
|
description: 'client name'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
image_tag:
|
||||||
|
description: 'image tag'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
image_name:
|
||||||
|
description: 'image registry and name'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
cosign:
|
||||||
|
required: false
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
description: 'run cosign on released image'
|
||||||
|
support_platforms:
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
type: boolean
|
||||||
|
description: 'support amd64/arm64'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-secret:
|
||||||
|
name: check if QUAYIO_REGISTRY_USERNAME & QUAYIO_REGISTRY_PASSWORD is set in github secrets
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
|
||||||
|
steps:
|
||||||
|
- name: Check whether unity activation requests should be done
|
||||||
|
id: check-secret-set
|
||||||
|
env:
|
||||||
|
QUAYIO_REGISTRY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
|
||||||
|
QUAYIO_REGISTRY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
|
||||||
|
run: |
|
||||||
|
echo "is-secret-set=${{ env.QUAYIO_REGISTRY_USERNAME != '' && env.QUAYIO_REGISTRY_PASSWORD != '' }}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
build-image:
|
||||||
|
needs: [check-secret]
|
||||||
|
if: needs.check-secret.outputs.is-secret-set == 'true'
|
||||||
|
name: Build image and upload to registry
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
|
||||||
|
- name: Login to Quay.io
|
||||||
|
env:
|
||||||
|
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
|
||||||
|
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
|
||||||
|
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
|
||||||
|
|
||||||
|
- name: Build and push image
|
||||||
|
if: ${{ inputs.support_platforms }}
|
||||||
|
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push --platform linux/amd64,linux/arm64
|
||||||
|
|
||||||
|
- name: Build and push image without amd64/arm64 support
|
||||||
|
if: ${{ !inputs.support_platforms }}
|
||||||
|
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push
|
||||||
|
|
||||||
|
- name: Install cosign
|
||||||
|
uses: sigstore/cosign-installer@main
|
||||||
|
with:
|
||||||
|
cosign-release: 'v1.12.0'
|
||||||
|
- name: sign kubescape container image
|
||||||
|
if: ${{ inputs.cosign }}
|
||||||
|
env:
|
||||||
|
COSIGN_EXPERIMENTAL: "true"
|
||||||
|
run: |
|
||||||
|
cosign sign --force ${{ inputs.image_name }}
|
||||||
|
|
||||||
140
.github/workflows/build.yaml
vendored
140
.github/workflows/build.yaml
vendored
@@ -2,53 +2,123 @@ name: build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches:
|
||||||
pull_request:
|
- 'master'
|
||||||
branches: [ master ]
|
- 'main'
|
||||||
types: [ closed ]
|
paths-ignore:
|
||||||
|
- '**.yaml'
|
||||||
|
- '**.md'
|
||||||
|
- '**.sh'
|
||||||
|
- 'website/*'
|
||||||
|
- 'examples/*'
|
||||||
|
- 'docs/*'
|
||||||
|
- 'build/*'
|
||||||
|
- '.github/*'
|
||||||
jobs:
|
jobs:
|
||||||
once:
|
test:
|
||||||
name: Create release
|
uses: ./.github/workflows/test.yaml
|
||||||
runs-on: ubuntu-latest
|
with:
|
||||||
outputs:
|
release: "v2.0.${{ github.run_number }}"
|
||||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
client: test
|
||||||
steps:
|
|
||||||
- name: Create a release
|
create-release:
|
||||||
id: create_release
|
uses: ./.github/workflows/release.yaml
|
||||||
uses: actions/create-release@v1
|
needs: test
|
||||||
env:
|
with:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
release_name: "Release v2.0.${{ github.run_number }}"
|
||||||
with:
|
tag_name: "v2.0.${{ github.run_number }}"
|
||||||
tag_name: v1.0.${{ github.run_number }}
|
secrets: inherit
|
||||||
release_name: Release v1.0.${{ github.run_number }}
|
|
||||||
draft: false
|
publish-artifacts:
|
||||||
prerelease: false
|
name: Build and publish artifacts
|
||||||
build:
|
needs: create-release
|
||||||
name: Create cross-platform release build, tag and upload binaries
|
|
||||||
needs: once
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v2
|
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Install MSYS2 & libgit2 (Windows)
|
||||||
|
shell: cmd
|
||||||
|
run: .\build.bat all
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
|
||||||
|
- name: Install libgit2 (Linux/macOS)
|
||||||
|
run: make libgit2
|
||||||
|
if: matrix.os != 'windows-latest'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5
|
env:
|
||||||
|
RELEASE: v2.0.${{ github.run_number }}
|
||||||
- name: Upload Release binaries
|
CLIENT: release
|
||||||
id: upload-release-asset
|
CGO_ENABLED: 1
|
||||||
|
run: python3 --version && python3 build.py
|
||||||
|
|
||||||
|
- name: Upload release binaries (Windows / MacOS)
|
||||||
|
id: upload-release-asset-win-macos
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ needs.once.outputs.upload_url }}
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
asset_path: build/${{ matrix.os }}/kubescape
|
asset_path: build/${{ matrix.os }}/kubescape
|
||||||
asset_name: kubescape-${{ matrix.os }}
|
asset_name: kubescape-${{ matrix.os }}
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
if: matrix.os != 'ubuntu-20.04'
|
||||||
|
|
||||||
|
- name: Upload release binaries (Linux)
|
||||||
|
id: upload-release-asset-linux
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: build/ubuntu-latest/kubescape
|
||||||
|
asset_name: kubescape-ubuntu-latest
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
|
|
||||||
|
- name: Upload release hash (Windows / MacOS)
|
||||||
|
id: upload-release-hash-win-macos
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: build/${{ matrix.os }}/kubescape.sha256
|
||||||
|
asset_name: kubescape-${{ matrix.os }}-sha256
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
if: matrix.os != 'ubuntu-20.04'
|
||||||
|
|
||||||
|
- name: Upload release hash (Linux)
|
||||||
|
id: upload-release-hash-linux
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: build/ubuntu-latest/kubescape.sha256
|
||||||
|
asset_name: kubescape-ubuntu-latest-sha256
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
|
|
||||||
|
publish-image:
|
||||||
|
uses: ./.github/workflows/build-image.yaml
|
||||||
|
needs: create-release
|
||||||
|
with:
|
||||||
|
client: "image-release"
|
||||||
|
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
|
||||||
|
image_tag: "v2.0.${{ github.run_number }}"
|
||||||
|
support_platforms: true
|
||||||
|
cosign: true
|
||||||
|
secrets: inherit
|
||||||
|
|||||||
31
.github/workflows/build_dev.yaml
vendored
Normal file
31
.github/workflows/build_dev.yaml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: build-dev
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ dev ]
|
||||||
|
paths-ignore:
|
||||||
|
- '**.yaml'
|
||||||
|
- '**.md'
|
||||||
|
- '**.sh'
|
||||||
|
- 'website/*'
|
||||||
|
- 'examples/*'
|
||||||
|
- 'docs/*'
|
||||||
|
- 'build/*'
|
||||||
|
- '.github/*'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
uses: ./.github/workflows/test.yaml
|
||||||
|
with:
|
||||||
|
release: "v2.0.${{ github.run_number }}"
|
||||||
|
client: test
|
||||||
|
|
||||||
|
# publish-dev-image:
|
||||||
|
# uses: ./.github/workflows/build-image.yaml
|
||||||
|
# needs: test
|
||||||
|
# with:
|
||||||
|
# client: "image-dev"
|
||||||
|
# image_name: "quay.io/${{ github.repository_owner }}/kubescape"
|
||||||
|
# image_tag: "dev-v2.0.${{ github.run_number }}"
|
||||||
|
# support_platforms: true
|
||||||
|
# cosign: true
|
||||||
|
# secrets: inherit
|
||||||
23
.github/workflows/close-typos-issues.yaml
vendored
Normal file
23
.github/workflows/close-typos-issues.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened, labeled]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
open_PR_message:
|
||||||
|
if: github.event.label.name == 'typo'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: ben-z/actions-comment-on-issue@1.0.2
|
||||||
|
with:
|
||||||
|
message: "Hello! :wave:\n\nThis issue is being automatically closed, Please open a PR with a relevant fix."
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
auto_close_issues:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: lee-dohm/close-matching-issues@v2
|
||||||
|
with:
|
||||||
|
query: 'label:typo'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
19
.github/workflows/post-release.yaml
vendored
Normal file
19
.github/workflows/post-release.yaml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: create release digests
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [ published]
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
once:
|
||||||
|
name: Creating digests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Digest
|
||||||
|
uses: MCJack123/ghaction-generate-release-hashes@v1
|
||||||
|
with:
|
||||||
|
hash-type: sha1
|
||||||
|
file-name: kubescape-release-digests
|
||||||
24
.github/workflows/pr_checks.yaml
vendored
Normal file
24
.github/workflows/pr_checks.yaml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: pr-checks
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [ edited, opened, synchronize, reopened ]
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
- 'dev'
|
||||||
|
paths-ignore:
|
||||||
|
- '**.yaml'
|
||||||
|
- '**.md'
|
||||||
|
- '**.sh'
|
||||||
|
- 'website/*'
|
||||||
|
- 'examples/*'
|
||||||
|
- 'docs/*'
|
||||||
|
- 'build/*'
|
||||||
|
- '.github/*'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
uses: ./.github/workflows/test.yaml
|
||||||
|
with:
|
||||||
|
release: "v2.0.${{ github.run_number }}"
|
||||||
|
client: test
|
||||||
41
.github/workflows/release.yaml
vendored
Normal file
41
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
release_name:
|
||||||
|
description: 'release'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
tag_name:
|
||||||
|
description: 'tag'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
draft:
|
||||||
|
description: 'create draft release'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
outputs:
|
||||||
|
upload_url:
|
||||||
|
description: "The first output string"
|
||||||
|
value: ${{ jobs.release.outputs.upload_url }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Create release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
steps:
|
||||||
|
- name: Create a release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ inputs.tag_name }}
|
||||||
|
release_name: ${{ inputs.release_name }}
|
||||||
|
draft: ${{ inputs.draft }}
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
100
.github/workflows/test.yaml
vendored
Normal file
100
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
release:
|
||||||
|
description: 'release'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
client:
|
||||||
|
description: 'Client name'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Create cross-platform build
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-20.04, macos-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Cache Go modules (Linux)
|
||||||
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.cache/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Cache Go modules (macOS)
|
||||||
|
if: matrix.os == 'macos-latest'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/Library/Caches/go-build
|
||||||
|
~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Cache Go modules (Windows)
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~\AppData\Local\go-build
|
||||||
|
~\go\pkg\mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.19
|
||||||
|
|
||||||
|
- name: Install MSYS2 & libgit2 (Windows)
|
||||||
|
shell: cmd
|
||||||
|
run: .\build.bat all
|
||||||
|
if: matrix.os == 'windows-latest'
|
||||||
|
|
||||||
|
- name: Install libgit2 (Linux/macOS)
|
||||||
|
run: make libgit2
|
||||||
|
if: matrix.os != 'windows-latest'
|
||||||
|
|
||||||
|
- name: Test core pkg
|
||||||
|
run: go test "-tags=static,gitenabled" -v ./...
|
||||||
|
|
||||||
|
- name: Test httphandler pkg
|
||||||
|
run: cd httphandler && go test "-tags=static,gitenabled" -v ./...
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
RELEASE: ${{ inputs.release }}
|
||||||
|
CLIENT: test
|
||||||
|
CGO_ENABLED: 1
|
||||||
|
run: python3 --version && python3 build.py
|
||||||
|
|
||||||
|
- name: Smoke Testing (Windows / MacOS)
|
||||||
|
env:
|
||||||
|
RELEASE: ${{ inputs.release }}
|
||||||
|
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||||
|
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
|
||||||
|
if: matrix.os != 'ubuntu-20.04'
|
||||||
|
|
||||||
|
- name: Smoke Testing (Linux)
|
||||||
|
env:
|
||||||
|
RELEASE: ${{ inputs.release }}
|
||||||
|
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||||
|
run: python3 smoke_testing/init.py ${PWD}/build/ubuntu-latest/kubescape
|
||||||
|
if: matrix.os == 'ubuntu-20.04'
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,5 +1,9 @@
|
|||||||
*.vs*
|
*.vs*
|
||||||
*go.sum*
|
|
||||||
*kubescape*
|
*kubescape*
|
||||||
*debug*
|
*debug*
|
||||||
.idea
|
*vendor*
|
||||||
|
*.pyc*
|
||||||
|
.idea
|
||||||
|
.history
|
||||||
|
ca.srl
|
||||||
|
*.out
|
||||||
|
|||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "git2go"]
|
||||||
|
path = git2go
|
||||||
|
url = https://github.com/libgit2/git2go.git
|
||||||
57
.golangci.yml
Normal file
57
.golangci.yml
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
check-shadowing: true
|
||||||
|
dupl:
|
||||||
|
threshold: 200
|
||||||
|
goconst:
|
||||||
|
min-len: 3
|
||||||
|
min-occurrences: 2
|
||||||
|
gocognit:
|
||||||
|
min-complexity: 65
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- gosec
|
||||||
|
- staticcheck
|
||||||
|
- nolintlint
|
||||||
|
- gofmt
|
||||||
|
- unused
|
||||||
|
- govet
|
||||||
|
- bodyclose
|
||||||
|
- typecheck
|
||||||
|
- goimports
|
||||||
|
- ineffassign
|
||||||
|
- gosimple
|
||||||
|
disable:
|
||||||
|
# temporarily disabled
|
||||||
|
- varcheck
|
||||||
|
- errcheck
|
||||||
|
- dupl
|
||||||
|
- gocritic
|
||||||
|
- gocognit
|
||||||
|
- nakedret
|
||||||
|
- revive
|
||||||
|
- stylecheck
|
||||||
|
- unconvert
|
||||||
|
- unparam
|
||||||
|
#- forbidigo # <- see later
|
||||||
|
# should remain disabled
|
||||||
|
- deadcode # deprecated linter
|
||||||
|
- maligned
|
||||||
|
- lll
|
||||||
|
- gochecknoinits
|
||||||
|
- gochecknoglobals
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "var-naming"
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "type name will be used as (.+?) by other packages, and that stutters"
|
||||||
|
- linters:
|
||||||
|
- stylecheck
|
||||||
|
text: "ST1003"
|
||||||
|
run:
|
||||||
|
skip-dirs:
|
||||||
|
- git2go
|
||||||
3
CODE_OF_CONDUCT.md
Normal file
3
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||||
64
CONTRIBUTING.md
Normal file
64
CONTRIBUTING.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
First, it is awesome that you are considering contributing to Kubescape! Contributing is important and fun and we welcome your efforts.
|
||||||
|
|
||||||
|
When contributing, we categorize contributions into two:
|
||||||
|
* Small code changes or fixes, whose scope is limited to a single or two files
|
||||||
|
* Complex features and improvements, with potentially unlimited scope
|
||||||
|
|
||||||
|
If you have a small change, feel free to fire up a Pull Request.
|
||||||
|
|
||||||
|
When planning a bigger change, please first discuss the change you wish to make via an issue,
|
||||||
|
so the maintainers are able to help guide you and let you know if you are going in the right direction.
|
||||||
|
|
||||||
|
## Code of Conduct
|
||||||
|
|
||||||
|
Please follow our [code of conduct](CODE_OF_CONDUCT.md) in all of your interactions within the project.
|
||||||
|
|
||||||
|
## Pull Request Process
|
||||||
|
|
||||||
|
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
|
||||||
|
build.
|
||||||
|
2. Update the README.md with details of changes to the interface, this includes new environment
|
||||||
|
variables, exposed ports, useful file locations and container parameters.
|
||||||
|
3. Open Pull Request to `dev` branch - we test the component before merging into the `master` branch
|
||||||
|
4. We will merge the Pull Request once you have the sign-off.
|
||||||
|
|
||||||
|
## Developer Certificate of Origin
|
||||||
|
|
||||||
|
All commits to the project must be "signed off", which states that you agree to the terms of the [Developer Certificate of Origin](https://developercertificate.org/). This is done by adding a "Signed-off-by:" line in the commit message, with your name and email address.
|
||||||
|
|
||||||
|
Commits made through the GitHub web application are automatically signed off.
|
||||||
|
|
||||||
|
### Configuring Git to sign off commits
|
||||||
|
|
||||||
|
First, configure your name and email address in Git global settings:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git config --global user.name "John Doe"
|
||||||
|
$ git config --global user.email johndoe@example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now sign off per-commit, or configure Git to always sign off commits per repository.
|
||||||
|
|
||||||
|
### Sign off per-commit
|
||||||
|
|
||||||
|
Add [`-s`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--s) to your Git command line. For example:
|
||||||
|
|
||||||
|
```git commit -s -m "Fix issue 64738"```
|
||||||
|
|
||||||
|
This is tedious, and if you forget, you'll have to [amend your commit](#f)
|
||||||
|
|
||||||
|
### Configure a repository to always include sign off
|
||||||
|
|
||||||
|
There are many ways to achieve this with Git hooks, but the simplest is to do the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd your-repo
|
||||||
|
curl -Ls https://gist.githubusercontent.com/dixudx/7d7edea35b4d91e1a2a8fbf41d0954fa/raw/prepare-commit-msg -o .git/hooks/prepare-commit-msg
|
||||||
|
chmod +x .git/hooks/prepare-commit-msg
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fixing a commit where the DCO failed
|
||||||
|
|
||||||
|
Check out [this guide](https://github.com/src-d/guide/blob/master/developer-community/fix-DCO.md).
|
||||||
11
MAINTAINERS.md
Normal file
11
MAINTAINERS.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Maintainers
|
||||||
|
|
||||||
|
The following table lists the Kubescape project maintainers:
|
||||||
|
|
||||||
|
| Name | GitHub | Organization | Added/Renewed On |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| [Ben Hirschberg](https://www.linkedin.com/in/benyamin-ben-hirschberg-66141890) | [@slashben](https://github.com/slashben) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
|
||||||
|
| [Rotem Refael](https://www.linkedin.com/in/rotem-refael) | [@rotemamsa](https://github.com/rotemamsa) | [ARMO](https://www.armosec.io/) | 2021-10-11 |
|
||||||
|
| [David Wertenteil](https://www.linkedin.com/in/david-wertenteil-0ba277b9) | [@dwertent](https://github.com/dwertent) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
|
||||||
|
| [Bezalel Brandwine](https://www.linkedin.com/in/bezalel-brandwine) | [@Bezbran](https://github.com/Bezbran) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
|
||||||
|
| [Craig Box](https://www.linkedin.com/in/crbnz/) | [@craigbox](https://github.com/craigbox) | [ARMO](https://www.armosec.io/) | 2022-10-31 |
|
||||||
20
Makefile
Normal file
20
Makefile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.PHONY: test all build libgit2
|
||||||
|
|
||||||
|
# default task invoked while running make
|
||||||
|
all: libgit2 build
|
||||||
|
|
||||||
|
export CGO_ENABLED=1
|
||||||
|
|
||||||
|
# build and install libgit2
|
||||||
|
libgit2:
|
||||||
|
-git submodule update --init --recursive
|
||||||
|
cd git2go; make install-static
|
||||||
|
|
||||||
|
# go build tags
|
||||||
|
TAGS = "gitenabled,static"
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build -v -tags=$(TAGS) .
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -v -tags=$(TAGS) ./...
|
||||||
212
README.md
212
README.md
@@ -1,176 +1,94 @@
|
|||||||
<img src="docs/kubescape.png" width="300" alt="logo" align="center">
|
[](releases)
|
||||||
|
[](https://github.com/kubescape/kubescape/actions/workflows/build.yaml)
|
||||||
|
[](https://goreportcard.com/report/github.com/kubescape/kubescape)
|
||||||
|
[](https://gitpod.io/#https://github.com/kubescape/kubescape)
|
||||||
|
[](https://github.com/kubescape/kubescape/blob/master/LICENSE)
|
||||||
|
[](https://landscape.cncf.io/card-mode?project=sandbox&selected=kubescape)
|
||||||
|
[](https://twitter.com/kubescape)
|
||||||
|
|
||||||
[](https://github.com/armosec/kubescape/actions/workflows/build.yaml)
|
# Kubescape
|
||||||
[](https://goreportcard.com/report/github.com/armosec/kubescape)
|
|
||||||
|
|
||||||
Kubescape is the first tool for testing if Kubernetes is deployed securely as defined in [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/cncf/artwork/master/projects/kubescape/stacked/white/kubescape-stacked-white.svg" width="150">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/cncf/artwork/master/projects/kubescape/stacked/color/kubescape-stacked-color.svg" width="150">
|
||||||
|
<img alt="Kubescape logo" align="right" src="https://raw.githubusercontent.com/cncf/artwork/master/projects/kubescape/stacked/color/kubescape-stacked-color.svg" width="150">
|
||||||
|
</picture>
|
||||||
|
|
||||||
Use Kubescape to test clusters or scan single YAML files and integrate it to your processes.
|
_An open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters_
|
||||||
|
|
||||||
<img src="docs/demo.gif">
|
Kubescape is an open-source Kubernetes security platform. It includes risk analysis, security compliance, and misconfiguration scanning. Targeted at the DevSecOps practitioner or platform engineer, it offers an easy-to-use CLI interface, flexible output formats, and automated scanning capabilities. It saves Kubernetes users and admins precious time, effort, and resources.
|
||||||
|
|
||||||
# TL;DR
|
Kubescape scans clusters, YAML files, and Helm charts. It detects misconfigurations according to multiple frameworks (including [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo/?utm_source=github&utm_medium=repository), [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) and the [CIS Benchmark](https://www.armosec.io/blog/cis-kubernetes-benchmark-framework-scanning-tools-comparison/?utm_source=github&utm_medium=repository)).
|
||||||
## Install & Run
|
|
||||||
|
|
||||||
### Install:
|
Kubescape was created by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository) and is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/).
|
||||||
```
|
|
||||||
curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh | /bin/bash
|
## Demo
|
||||||
|
<img src="docs/img/demo.gif">
|
||||||
|
|
||||||
|
_Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape! 😀_
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
Experimenting with Kubescape is as easy as:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run:
|
Learn more about:
|
||||||
```
|
|
||||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to scan all namespaces in your cluster, remove the `--exclude-namespaces` flag.
|
* [Installing Kubescape](docs/getting-started.md#install-kubescape)
|
||||||
|
* [Running your first scan](docs/getting-started.md#run-your-first-scan)
|
||||||
|
* [Usage](docs/getting-started.md#examples)
|
||||||
|
* [Architecture](docs/architecture.md)
|
||||||
|
* [Building Kubescape from source](docs/building.md)
|
||||||
|
|
||||||
<img src="docs/summary.png">
|
_Did you know you can use Kubescape in all these places?_
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="docs/img/ksfromcodetodeploy.png" alt="Places you can use Kubescape: in your IDE, CI, CD, or against a running cluster.">
|
||||||
|
</div>
|
||||||
|
|
||||||
### Flags
|
## Under the hood
|
||||||
|
|
||||||
| flag | default | description | options |
|
Kubescape uses [Open Policy Agent](https://github.com/open-policy-agent/opa) to verify Kubernetes objects against [a library of posture controls](https://github.com/kubescape/regolibrary).
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `-e`/`--exclude-namespaces` | Scan all namespaces | Namespaces to exclude from scanning. Recommended to exclude `kube-system` and `kube-public` namespaces |
|
|
||||||
| `-s`/`--silent` | Display progress messages | Silent progress messages |
|
|
||||||
| `-t`/`--fail-threshold` | `0` (do not fail) | fail command (return exit code 1) if result bellow threshold| `0` -> `100` |
|
|
||||||
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit` |
|
|
||||||
| `-o`/`--output` | print to stdout | Save scan result in file |
|
|
||||||
| `--use-from` | | Load local framework object from specified path. If not used will download latest |
|
|
||||||
| `--use-default` | `false` | Load local framework object from default path. If not used will download latest | `true`/`false` |
|
|
||||||
| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json) |
|
|
||||||
|
|
||||||
## Usage & Examples
|
By default, the results are printed in a console-friendly manner, but they can be:
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
* Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework
|
* exported to JSON or junit XML
|
||||||
```
|
* rendered to HTML or PDF
|
||||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
* submitted to a [cloud service](docs/providers.md)
|
||||||
```
|
|
||||||
|
|
||||||
* Scan local `yaml`/`json` files before deploying
|
It retrieves Kubernetes objects from the API server and runs a set of [Rego snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
|
||||||
```
|
|
||||||
kubescape scan framework nsa *.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
|
## Community
|
||||||
|
|
||||||
* Scan `yaml`/`json` files from url
|
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We are part of the Kubernetes community and are building more tests and controls as the ecosystem develops.
|
||||||
```
|
|
||||||
kubescape scan framework nsa https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/master/release/kubernetes-manifests.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
* Output in `json` format
|
We hold [community meetings](https://us02web.zoom.us/j/84020231442) on Zoom, on the first Tuesday of every month, at 14:00 GMT.
|
||||||
```
|
|
||||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format json --output results.json
|
|
||||||
```
|
|
||||||
|
|
||||||
* Output in `junit xml` format
|
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||||
```
|
|
||||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml
|
|
||||||
```
|
|
||||||
|
|
||||||
* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` <img src="docs/new-feature.svg">
|
## Contributions
|
||||||
```
|
|
||||||
kubescape scan framework nsa --exceptions examples/exceptions.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Helm Support
|
Thanks to all our contributors! Check out our [CONTRIBUTING](CONTRIBUTING.md) file to learn how to join them.
|
||||||
|
|
||||||
* Render the helm chart using [`helm template`](https://helm.sh/docs/helm/helm_template/) and pass to stdout
|
* Feel free to pick a task from the [issues](https://github.com/kubescape/kubescape/issues?q=is%3Aissue+is%3Aopen+label%3A%22open+for+contribution%22), [roadmap](docs/roadmap.md) or suggest a feature of your own.
|
||||||
```
|
* [Open an issue](https://github.com/kubescape/kubescape/issues/new/choose): we aim to respond to all issues within 48 hours.
|
||||||
helm template [NAME] [CHART] [flags] --dry-run | kubescape scan framework nsa -
|
* [Join the CNCF Slack](https://slack.cncf.io/) and then our [users](https://cloud-native.slack.com/archives/C04EY3ZF9GE) or [developers](https://cloud-native.slack.com/archives/C04GY6H082K) channel.
|
||||||
```
|
|
||||||
|
|
||||||
for example:
|
<br>
|
||||||
```
|
|
||||||
helm template bitnami/mysql --generate-name --dry-run | kubescape scan framework nsa -
|
|
||||||
```
|
|
||||||
|
|
||||||
### Offline Support <img src="docs/new-feature.svg">
|
<a href = "https://github.com/kubescape/kubescape/graphs/contributors">
|
||||||
|
<img src = "https://contrib.rocks/image?repo=kubescape/kubescape"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
It is possible to run Kubescape offline!
|
## License
|
||||||
|
|
||||||
First download the framework and then scan with `--use-from` flag
|
Copyright 2021-2023, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
* Download and save in file, if file name not specified, will store save to `~/.kubescape/<framework name>.json`
|
|
||||||
```
|
|
||||||
kubescape download framework nsa --output nsa.json
|
|
||||||
```
|
|
||||||
|
|
||||||
* Scan using the downloaded framework
|
|
||||||
```
|
|
||||||
kubescape scan framework nsa --use-from nsa.json
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# How to build
|
|
||||||
|
|
||||||
Note: development (and the release process) is done with Go `1.16`
|
|
||||||
|
|
||||||
1. Clone Project
|
|
||||||
```
|
|
||||||
git clone git@github.com:armosec/kubescape.git kubescape && cd "$_"
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Build
|
|
||||||
```
|
|
||||||
go mod tidy && go build -o kubescape .
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Run
|
|
||||||
```
|
|
||||||
./kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Enjoy :zany_face:
|
|
||||||
|
|
||||||
# How to build in Docker
|
|
||||||
|
|
||||||
1. Clone Project
|
|
||||||
```
|
|
||||||
git clone git@github.com:armosec/kubescape.git kubescape && cd "$_"
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Build
|
|
||||||
```
|
|
||||||
docker build -t kubescape -f build/Dockerfile .
|
|
||||||
```
|
|
||||||
|
|
||||||
# Under the hood
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
Kubescape is running the following tests according to what is defined by [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
|
||||||
* Non-root containers
|
|
||||||
* Immutable container filesystem
|
|
||||||
* Privileged containers
|
|
||||||
* hostPID, hostIPC privileges
|
|
||||||
* hostNetwork access
|
|
||||||
* allowedHostPaths field
|
|
||||||
* Protecting pod service account tokens
|
|
||||||
* Resource policies
|
|
||||||
* Control plane hardening
|
|
||||||
* Exposed dashboard
|
|
||||||
* Allow privilege escalation
|
|
||||||
* Applications credentials in configuration files
|
|
||||||
* Cluster-admin binding
|
|
||||||
* Exec into container
|
|
||||||
* Dangerous capabilities
|
|
||||||
* Insecure capabilities
|
|
||||||
* Linux hardening
|
|
||||||
* Ingress and Egress blocked
|
|
||||||
* Container hostPort
|
|
||||||
* Network policies
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Technology
|
|
||||||
Kubescape based on OPA engine: https://github.com/open-policy-agent/opa and ARMO's posture controls.
|
|
||||||
|
|
||||||
The tools retrieves Kubernetes objects from the API server and runs a set of [regos snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io/).
|
|
||||||
|
|
||||||
The results by default printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing.
|
|
||||||
|
|
||||||
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We’re also aiming to collaborate with the Kubernetes community to help make the tests themselves more robust and complete as Kubernetes develops.
|
|
||||||
|
|
||||||
|
Kubescape is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/) and was contributed by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository).
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="https://raw.githubusercontent.com/cncf/artwork/master/other/cncf-sandbox/horizontal/color/cncf-sandbox-horizontal-color.svg" width="300" alt="CNCF Sandbox Project">
|
||||||
|
</div>
|
||||||
|
|||||||
51
build.bat
Normal file
51
build.bat
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
IF "%1"=="install" goto Install
|
||||||
|
IF "%1"=="build" goto Build
|
||||||
|
IF "%1"=="all" goto All
|
||||||
|
IF "%1"=="" goto Error ELSE goto Error
|
||||||
|
|
||||||
|
:Install
|
||||||
|
|
||||||
|
if exist C:\MSYS64\ (
|
||||||
|
echo "MSYS2 already installed"
|
||||||
|
) else (
|
||||||
|
mkdir temp_install & cd temp_install
|
||||||
|
|
||||||
|
echo "Downloading MSYS2..."
|
||||||
|
curl -L https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-x86_64-20220603.exe > msys2-x86_64-20220603.exe
|
||||||
|
|
||||||
|
echo "Installing MSYS2..."
|
||||||
|
msys2-x86_64-20220603.exe install --root C:\MSYS64 --confirm-command
|
||||||
|
|
||||||
|
cd .. && rmdir /s /q temp_install
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
echo "Adding MSYS2 to path..."
|
||||||
|
SET "PATH=C:\MSYS64\mingw64\bin;C:\MSYS64\usr\bin;%PATH%"
|
||||||
|
echo %PATH%
|
||||||
|
|
||||||
|
echo "Installing MSYS2 packages..."
|
||||||
|
pacman -S --needed --noconfirm make
|
||||||
|
pacman -S --needed --noconfirm mingw-w64-x86_64-cmake
|
||||||
|
pacman -S --needed --noconfirm mingw-w64-x86_64-gcc
|
||||||
|
pacman -S --needed --noconfirm mingw-w64-x86_64-pkg-config
|
||||||
|
pacman -S --needed --noconfirm msys2-w32api-runtime
|
||||||
|
|
||||||
|
IF "%1"=="all" GOTO Build
|
||||||
|
GOTO End
|
||||||
|
|
||||||
|
:Build
|
||||||
|
SET "PATH=C:\MSYS2\mingw64\bin;C:\MSYS2\usr\bin;%PATH%"
|
||||||
|
make libgit2
|
||||||
|
GOTO End
|
||||||
|
|
||||||
|
:All
|
||||||
|
GOTO Install
|
||||||
|
|
||||||
|
:Error
|
||||||
|
echo "Error: Unknown option"
|
||||||
|
GOTO End
|
||||||
|
|
||||||
|
:End
|
||||||
80
build.py
Normal file
80
build.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import hashlib
|
||||||
|
import platform
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
BASE_GETTER_CONST = "github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||||
|
|
||||||
|
def check_status(status, msg):
|
||||||
|
if status != 0:
|
||||||
|
sys.stderr.write(msg)
|
||||||
|
exit(status)
|
||||||
|
|
||||||
|
|
||||||
|
def get_build_dir():
|
||||||
|
current_platform = platform.system()
|
||||||
|
build_dir = ""
|
||||||
|
|
||||||
|
if current_platform == "Windows": build_dir = "windows-latest"
|
||||||
|
elif current_platform == "Linux": build_dir = "ubuntu-latest"
|
||||||
|
elif current_platform == "Darwin": build_dir = "macos-latest"
|
||||||
|
else: raise OSError("Platform %s is not supported!" % (current_platform))
|
||||||
|
|
||||||
|
return os.path.join("build", build_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def get_package_name():
|
||||||
|
package_name = "kubescape"
|
||||||
|
|
||||||
|
return package_name
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Building Kubescape")
|
||||||
|
|
||||||
|
# Set some variables
|
||||||
|
package_name = get_package_name()
|
||||||
|
build_url = "github.com/kubescape/kubescape/v2/core/cautils.BuildNumber"
|
||||||
|
release_version = os.getenv("RELEASE")
|
||||||
|
|
||||||
|
client_var = "github.com/kubescape/kubescape/v2/core/cautils.Client"
|
||||||
|
client_name = os.getenv("CLIENT")
|
||||||
|
|
||||||
|
# Create build directory
|
||||||
|
build_dir = get_build_dir()
|
||||||
|
|
||||||
|
ks_file = os.path.join(build_dir, package_name)
|
||||||
|
hash_file = ks_file + ".sha256"
|
||||||
|
|
||||||
|
if not os.path.isdir(build_dir):
|
||||||
|
os.makedirs(build_dir)
|
||||||
|
|
||||||
|
# Build kubescape
|
||||||
|
ldflags = "-w -s"
|
||||||
|
if release_version:
|
||||||
|
ldflags += " -X {}={}".format(build_url, release_version)
|
||||||
|
if client_name:
|
||||||
|
ldflags += " -X {}={}".format(client_var, client_name)
|
||||||
|
|
||||||
|
build_command = ["go", "build", "-buildmode=pie", "-tags=static,gitenabled", "-o", ks_file, "-ldflags" ,ldflags]
|
||||||
|
|
||||||
|
print("Building kubescape and saving here: {}".format(ks_file))
|
||||||
|
print("Build command: {}".format(" ".join(build_command)))
|
||||||
|
|
||||||
|
status = subprocess.call(build_command)
|
||||||
|
check_status(status, "Failed to build kubescape")
|
||||||
|
|
||||||
|
sha256 = hashlib.sha256()
|
||||||
|
with open(ks_file, "rb") as kube:
|
||||||
|
sha256.update(kube.read())
|
||||||
|
with open(hash_file, "w") as kube_sha:
|
||||||
|
hash = sha256.hexdigest()
|
||||||
|
print("kubescape hash: {}, file: {}".format(hash, hash_file))
|
||||||
|
kube_sha.write(sha256.hexdigest())
|
||||||
|
|
||||||
|
print("Build Done")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,13 +1,51 @@
|
|||||||
FROM golang:1.16-alpine as builder
|
FROM golang:1.19-alpine as builder
|
||||||
ENV GOPROXY=https://goproxy.io,direct
|
|
||||||
ENV GO111MODULE=on
|
ARG image_version
|
||||||
|
ARG client
|
||||||
|
|
||||||
|
ENV RELEASE=$image_version
|
||||||
|
ENV CLIENT=$client
|
||||||
|
|
||||||
|
ENV GO111MODULE=
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
# Install required python/pip
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
RUN apk add --update --no-cache python3 gcc make git libc-dev binutils-gold cmake pkgconfig && ln -sf python3 /usr/bin/python
|
||||||
|
RUN python3 -m ensurepip
|
||||||
|
RUN pip3 install --no-cache --upgrade pip setuptools
|
||||||
|
|
||||||
WORKDIR /work
|
WORKDIR /work
|
||||||
ADD . .
|
ADD . .
|
||||||
RUN go mod tidy
|
|
||||||
RUN GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w " -installsuffix cgo -o kubescape .
|
|
||||||
|
|
||||||
FROM alpine
|
# install libgit2
|
||||||
COPY --from=builder /work/kubescape /usr/bin/kubescape
|
RUN rm -rf git2go && make libgit2
|
||||||
|
|
||||||
CMD ["kubescape"]
|
# build kubescape server
|
||||||
|
WORKDIR /work/httphandler
|
||||||
|
RUN python build.py
|
||||||
|
RUN ls -ltr build/ubuntu-latest
|
||||||
|
|
||||||
|
# build kubescape cmd
|
||||||
|
WORKDIR /work
|
||||||
|
RUN python build.py
|
||||||
|
|
||||||
|
RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
|
||||||
|
|
||||||
|
FROM alpine:3.16.2
|
||||||
|
|
||||||
|
RUN addgroup -S ks && adduser -S ks -G ks
|
||||||
|
|
||||||
|
COPY --from=builder /work/artifacts/ /home/ks/.kubescape
|
||||||
|
|
||||||
|
RUN chown -R ks:ks /home/ks/.kubescape
|
||||||
|
|
||||||
|
USER ks
|
||||||
|
|
||||||
|
WORKDIR /home/ks
|
||||||
|
|
||||||
|
COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/ksserver
|
||||||
|
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
|
||||||
|
|
||||||
|
ENTRYPOINT ["ksserver"]
|
||||||
|
|||||||
13
build/README.md
Normal file
13
build/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
## Docker Build
|
||||||
|
|
||||||
|
### Build your own Docker image
|
||||||
|
|
||||||
|
1. Clone Project
|
||||||
|
```
|
||||||
|
git clone https://github.com/kubescape/kubescape.git kubescape && cd "$_"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build
|
||||||
|
```
|
||||||
|
docker build -t kubescape -f build/Dockerfile .
|
||||||
|
```
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// HTTPReqFunc allows you to insert query params and more to aggregation message while using update aggregator
|
|
||||||
type HTTPReqFunc func(req *http.Request, qryData interface{})
|
|
||||||
|
|
||||||
func BasicBEQuery(req *http.Request, qryData interface{}) {
|
|
||||||
|
|
||||||
q := req.URL.Query()
|
|
||||||
|
|
||||||
if notificationData, isok := qryData.(*LoginObject); isok {
|
|
||||||
q.Add("customerGUID", notificationData.GUID)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func EmptyQuery(req *http.Request, qryData interface{}) {
|
|
||||||
q := req.URL.Query()
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func MapQuery(req *http.Request, qryData interface{}) {
|
|
||||||
q := req.URL.Query()
|
|
||||||
if qryMap, isok := qryData.(map[string]string); isok {
|
|
||||||
for k, v := range qryMap {
|
|
||||||
q.Add(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
func BEHttpRequest(loginobj *LoginObject, beURL,
|
|
||||||
httpverb string,
|
|
||||||
endpoint string,
|
|
||||||
payload []byte,
|
|
||||||
f HTTPReqFunc,
|
|
||||||
qryData interface{}) ([]byte, error) {
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
beURL = fmt.Sprintf("%v/%v", beURL, endpoint)
|
|
||||||
req, err := http.NewRequest(httpverb, beURL, bytes.NewReader(payload))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Authorization", loginobj.Authorization)
|
|
||||||
f(req, qryData)
|
|
||||||
|
|
||||||
for _, cookie := range loginobj.Cookies {
|
|
||||||
req.AddCookie(cookie)
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
fmt.Printf("req:\n%v\nresp:%v\n", req, resp)
|
|
||||||
return nil, fmt.Errorf("Error #%v Due to: %v", resp.StatusCode, resp.Status)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type BELoginResponse struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
PreferredUsername string `json:"preferred_username"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
CustomerGuid string `json:"customerGuid"`
|
|
||||||
Expires string `json:"expires"`
|
|
||||||
Authorization string `json:"authorization"`
|
|
||||||
Cookies []*http.Cookie
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BELoginResponse) ToLoginObject() *LoginObject {
|
|
||||||
l := &LoginObject{}
|
|
||||||
l.Authorization = r.Authorization
|
|
||||||
l.Cookies = r.Cookies
|
|
||||||
l.Expires = r.Expires
|
|
||||||
l.GUID = r.CustomerGuid
|
|
||||||
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
type BackendConnector struct {
|
|
||||||
BaseURL string
|
|
||||||
BELoginResponse *BELoginResponse
|
|
||||||
Credentials *CustomerLoginDetails
|
|
||||||
HTTPClient *http.Client
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func MakeBackendConnector(client *http.Client, baseURL string, loginDetails *CustomerLoginDetails) (*BackendConnector, error) {
|
|
||||||
if err := ValidateBEConnectorMakerInput(client, baseURL, loginDetails); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
conn := &BackendConnector{BaseURL: baseURL, Credentials: loginDetails, HTTPClient: client}
|
|
||||||
err := conn.Login()
|
|
||||||
|
|
||||||
return conn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateBEConnectorMakerInput(client *http.Client, baseURL string, loginDetails *CustomerLoginDetails) error {
|
|
||||||
if client == nil {
|
|
||||||
fmt.Errorf("You must provide an initialized httpclient")
|
|
||||||
}
|
|
||||||
if len(baseURL) == 0 {
|
|
||||||
return fmt.Errorf("you must provide a valid backend url")
|
|
||||||
}
|
|
||||||
|
|
||||||
if loginDetails == nil || (len(loginDetails.Email) == 0 && len(loginDetails.Password) == 0) {
|
|
||||||
return fmt.Errorf("you must provide valid login details")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BackendConnector) Login() error {
|
|
||||||
if !r.IsExpired() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
loginInfoBytes, err := json.Marshal(r.Credentials)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to marshal credentials properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
beURL := fmt.Sprintf("%v/%v", r.BaseURL, "login")
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", beURL, bytes.NewReader(loginInfoBytes))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Referer", strings.Replace(beURL, "dashbe", "cpanel", 1))
|
|
||||||
resp, err := r.HTTPClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to read login response")
|
|
||||||
}
|
|
||||||
|
|
||||||
loginS := &BELoginResponse{}
|
|
||||||
json.Unmarshal(body, &loginS)
|
|
||||||
|
|
||||||
loginS.Cookies = resp.Cookies()
|
|
||||||
r.BELoginResponse = loginS
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BackendConnector) IsExpired() bool {
|
|
||||||
return r.BELoginResponse == nil || r.BELoginResponse.ToLoginObject().IsExpired()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BackendConnector) GetBaseURL() string {
|
|
||||||
return r.BaseURL
|
|
||||||
}
|
|
||||||
func (r *BackendConnector) GetLoginObj() *LoginObject {
|
|
||||||
return r.BELoginResponse.ToLoginObject()
|
|
||||||
}
|
|
||||||
func (r *BackendConnector) GetClient() *http.Client {
|
|
||||||
return r.HTTPClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *BackendConnector) HTTPSend(httpverb string,
|
|
||||||
endpoint string,
|
|
||||||
payload []byte,
|
|
||||||
f HTTPReqFunc,
|
|
||||||
qryData interface{}) ([]byte, error) {
|
|
||||||
|
|
||||||
beURL := fmt.Sprintf("%v/%v", r.GetBaseURL(), endpoint)
|
|
||||||
req, err := http.NewRequest(httpverb, beURL, bytes.NewReader(payload))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.IsExpired() {
|
|
||||||
r.Login()
|
|
||||||
}
|
|
||||||
|
|
||||||
loginobj := r.GetLoginObj()
|
|
||||||
req.Header.Set("Authorization", loginobj.Authorization)
|
|
||||||
f(req, qryData)
|
|
||||||
q := req.URL.Query()
|
|
||||||
q.Set("customerGUID", loginobj.GUID)
|
|
||||||
req.URL.RawQuery = q.Encode()
|
|
||||||
|
|
||||||
for _, cookie := range loginobj.Cookies {
|
|
||||||
req.AddCookie(cookie)
|
|
||||||
}
|
|
||||||
resp, err := r.GetClient().Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
fmt.Printf("req:\n%v\nresp:%v\n", req, resp)
|
|
||||||
return nil, fmt.Errorf("Error #%v Due to: %v", resp.StatusCode, resp.Status)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return body, nil
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
// WebsocketScanCommand api
|
|
||||||
const (
|
|
||||||
WebsocketScanCommandVersion string = "v1"
|
|
||||||
WebsocketScanCommandPath string = "scanImage"
|
|
||||||
)
|
|
||||||
|
|
||||||
// commands send via websocket
|
|
||||||
const (
|
|
||||||
UPDATE string = "update"
|
|
||||||
ATTACH string = "Attach"
|
|
||||||
REMOVE string = "remove"
|
|
||||||
DETACH string = "Detach"
|
|
||||||
INCOMPATIBLE string = "Incompatible"
|
|
||||||
REPLACE_HEADERS string = "ReplaceHeaders"
|
|
||||||
IMAGE_UNREACHABLE string = "ImageUnreachable"
|
|
||||||
SIGN string = "sign"
|
|
||||||
UNREGISTERED string = "unregistered"
|
|
||||||
INJECT string = "inject"
|
|
||||||
RESTART string = "restart"
|
|
||||||
ENCRYPT string = "encryptSecret"
|
|
||||||
DECRYPT string = "decryptSecret"
|
|
||||||
SCAN string = "scan"
|
|
||||||
)
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WebsocketScanCommand trigger scan thru the websocket
|
|
||||||
type WebsocketScanCommand struct {
|
|
||||||
// CustomerGUID string `json:"customerGUID"`
|
|
||||||
ImageTag string `json:"imageTag"`
|
|
||||||
Wlid string `json:"wlid"`
|
|
||||||
IsScanned bool `json:"isScanned"`
|
|
||||||
ContainerName string `json:"containerName"`
|
|
||||||
JobID string `json:"jobID,omitempty"`
|
|
||||||
LastAction int `json:"actionIDN"`
|
|
||||||
// ImageHash string `json:"imageHash"`
|
|
||||||
Credentials *types.AuthConfig `json:"credentials,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
//taken from BE
|
|
||||||
// ElasticRespTotal holds the total struct in Elastic array response
|
|
||||||
type ElasticRespTotal struct {
|
|
||||||
Value int `json:"value"`
|
|
||||||
Relation string `json:"relation"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// V2ListResponse holds the response of some list request with some metadata
|
|
||||||
type V2ListResponse struct {
|
|
||||||
Total ElasticRespTotal `json:"total"`
|
|
||||||
Response interface{} `json:"response"`
|
|
||||||
// Cursor for quick access to the next page. Not supported yet
|
|
||||||
Cursor string `json:"cursor"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Oauth2Customer returns inside the "ca_groups" field in claims section of
|
|
||||||
// Oauth2 verification process
|
|
||||||
type Oauth2Customer struct {
|
|
||||||
CustomerName string `json:"customerName"`
|
|
||||||
CustomerGUID string `json:"customerGUID"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginObject struct {
|
|
||||||
Authorization string `json:"authorization"`
|
|
||||||
GUID string
|
|
||||||
Cookies []*http.Cookie
|
|
||||||
Expires string
|
|
||||||
}
|
|
||||||
|
|
||||||
type SafeMode struct {
|
|
||||||
Reporter string `json:"reporter"` // "Agent"
|
|
||||||
Action string `json:"action,omitempty"` // "action"
|
|
||||||
Wlid string `json:"wlid"` // CAA_WLID
|
|
||||||
PodName string `json:"podName"` // CAA_POD_NAME
|
|
||||||
InstanceID string `json:"instanceID"` // CAA_POD_NAME
|
|
||||||
ContainerName string `json:"containerName,omitempty"` // CAA_CONTAINER_NAME
|
|
||||||
ProcessName string `json:"processName,omitempty"`
|
|
||||||
ProcessID int `json:"processID,omitempty"`
|
|
||||||
ProcessCMD string `json:"processCMD,omitempty"`
|
|
||||||
ComponentGUID string `json:"componentGUID,omitempty"` // CAA_GUID
|
|
||||||
StatusCode int `json:"statusCode"` // 0/1/2
|
|
||||||
ProcessExitCode int `json:"processExitCode"` // 0 +
|
|
||||||
Timestamp int64 `json:"timestamp"`
|
|
||||||
Message string `json:"message,omitempty"` // any string
|
|
||||||
JobID string `json:"jobID,omitempty"` // any string
|
|
||||||
Compatible *bool `json:"compatible,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (safeMode *SafeMode) Json() string {
|
|
||||||
b, err := json.Marshal(*safeMode)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%s", b)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
// import (
|
|
||||||
// "fmt"
|
|
||||||
// "net/http"
|
|
||||||
// "testing"
|
|
||||||
// )
|
|
||||||
|
|
||||||
// func TestAuditStructure(t *testing.T) {
|
|
||||||
// c := http.Client{}
|
|
||||||
// be, err := MakeBackendConnector(&c, "https://dashbe.eudev3.cyberarmorsoft.com", &CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "*", CustomerName: "CyberArmorTests"})
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("sad1")
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// b, err := be.HTTPSend("GET", "v1/microservicesOverview", nil, MapQuery, map[string]string{"wlid": "wlid://cluster-childrenofbodom/namespace-default/deployment-pos"})
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("sad2")
|
|
||||||
|
|
||||||
// }
|
|
||||||
// fmt.Printf("%v", string(b))
|
|
||||||
|
|
||||||
// t.Errorf("sad")
|
|
||||||
|
|
||||||
// }
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
// type Dashboard interface {
|
|
||||||
// OPAFRAMEWORKGet(string, bool) ([]opapolicy.Framework, error)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Connector - interface for any connector (BE/Portal and so on)
|
|
||||||
type Connector interface {
|
|
||||||
|
|
||||||
//may used for a more generic httpsend interface based method
|
|
||||||
GetBaseURL() string
|
|
||||||
GetLoginObj() *LoginObject
|
|
||||||
GetClient() *http.Client
|
|
||||||
|
|
||||||
Login() error
|
|
||||||
IsExpired() bool
|
|
||||||
|
|
||||||
HTTPSend(httpverb string,
|
|
||||||
endpoint string,
|
|
||||||
payload []byte,
|
|
||||||
f HTTPReqFunc,
|
|
||||||
qryData interface{}) ([]byte, error)
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
oidc "github.com/coreos/go-oidc"
|
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
|
|
||||||
// "go.uber.org/zap"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetOauth2TokenURL() string {
|
|
||||||
return "https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites"
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLoginStruct() (LoginAux, error) {
|
|
||||||
|
|
||||||
return LoginAux{Referer: "https://cpanel.eudev3.cyberarmorsoft.com/login", Url: "https://cpanel.eudev3.cyberarmorsoft.com/login"}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoginWithKeycloak(loginDetails CustomerLoginDetails) ([]uuid.UUID, *oidc.IDToken, error) {
|
|
||||||
// var custGUID uuid.UUID
|
|
||||||
// config.Oauth2TokenURL
|
|
||||||
if GetOauth2TokenURL() == "" {
|
|
||||||
return nil, nil, fmt.Errorf("missing oauth2 token URL")
|
|
||||||
}
|
|
||||||
urlaux, _ := GetLoginStruct()
|
|
||||||
conf, err := getOauth2Config(urlaux)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
ctx := context.Background()
|
|
||||||
provider, err := oidc.NewProvider(ctx, GetOauth2TokenURL())
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// "Oauth2ClientID": "golang-client"
|
|
||||||
oidcConfig := &oidc.Config{
|
|
||||||
ClientID: "golang-client",
|
|
||||||
SkipClientIDCheck: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
verifier := provider.Verifier(oidcConfig)
|
|
||||||
ouToken, err := conf.PasswordCredentialsToken(ctx, loginDetails.Email, loginDetails.Password)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// "Authorization",
|
|
||||||
authorization := fmt.Sprintf("%s %s", ouToken.Type(), ouToken.AccessToken)
|
|
||||||
// oidc.IDTokenVerifier
|
|
||||||
tkn, err := verifier.Verify(ctx, ouToken.AccessToken)
|
|
||||||
if err != nil {
|
|
||||||
return nil, tkn, err
|
|
||||||
}
|
|
||||||
tkn.Nonce = authorization
|
|
||||||
if loginDetails.CustomerName == "" {
|
|
||||||
customers, err := getCustomersNames(tkn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, tkn, err
|
|
||||||
}
|
|
||||||
if len(customers) == 1 {
|
|
||||||
loginDetails.CustomerName = customers[0]
|
|
||||||
} else {
|
|
||||||
return nil, tkn, fmt.Errorf("login with one of the following customers: %v", customers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
custGUID, err := getCustomerGUID(tkn, &loginDetails)
|
|
||||||
if err != nil {
|
|
||||||
return nil, tkn, err
|
|
||||||
}
|
|
||||||
return []uuid.UUID{custGUID}, tkn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOauth2Config(urlaux LoginAux) (*oauth2.Config, error) {
|
|
||||||
reURLSlices := strings.Split(urlaux.Referer, "/")
|
|
||||||
if len(reURLSlices) == 0 {
|
|
||||||
reURLSlices = strings.Split(urlaux.Url, "/")
|
|
||||||
}
|
|
||||||
// zapLogger.With(zap.Strings("referer", reURLSlices)).Info("Searching oauth2Config for")
|
|
||||||
if len(reURLSlices) < 3 {
|
|
||||||
reURLSlices = []string{reURLSlices[0], reURLSlices[0], reURLSlices[0]}
|
|
||||||
}
|
|
||||||
lg, _ := GetLoginStruct()
|
|
||||||
provider, _ := oidc.NewProvider(context.Background(), GetOauth2TokenURL())
|
|
||||||
//provider.Endpoint {"AuthURL":"https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites/protocol/openid-connect/auth","TokenURL":"https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites/protocol/openid-connect/token","AuthStyle":0}
|
|
||||||
conf := oauth2.Config{
|
|
||||||
ClientID: "golang-client",
|
|
||||||
ClientSecret: "4e33bad2-3491-41a6-b486-93c492cfb4a2",
|
|
||||||
RedirectURL: lg.Referer,
|
|
||||||
// Discovery returns the OAuth2 endpoints.
|
|
||||||
Endpoint: provider.Endpoint(),
|
|
||||||
// "openid" is a required scope for OpenID Connect flows.
|
|
||||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
|
||||||
}
|
|
||||||
return &conf, nil
|
|
||||||
// return nil, fmt.Errorf("canno't find oauth2Config for referer '%+v'.\nPlease set referer or origin headers", reURLSlices)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCustomersNames(oauth2Details *oidc.IDToken) ([]string, error) {
|
|
||||||
var claimsJSON Oauth2Claims
|
|
||||||
if err := oauth2Details.Claims(&claimsJSON); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
customersList := make([]string, 0, len(claimsJSON.CAGroups))
|
|
||||||
for _, v := range claimsJSON.CAGroups {
|
|
||||||
var caCustomer Oauth2Customer
|
|
||||||
if err := json.Unmarshal([]byte(v), &caCustomer); err == nil {
|
|
||||||
customersList = append(customersList, caCustomer.CustomerName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return customersList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCustomerGUID(tkn *oidc.IDToken, loginDetails *CustomerLoginDetails) (uuid.UUID, error) {
|
|
||||||
|
|
||||||
customers, err := getCustomersList(tkn)
|
|
||||||
if err != nil {
|
|
||||||
return uuid.UUID{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if customer name not provided - use default customer
|
|
||||||
if loginDetails.CustomerName == "" && len(customers) > 0 {
|
|
||||||
return uuid.FromString(customers[0].CustomerGUID)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range customers {
|
|
||||||
if i.CustomerName == loginDetails.CustomerName {
|
|
||||||
return uuid.FromString(i.CustomerGUID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uuid.UUID{}, fmt.Errorf("customer name not found in customer list")
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCustomersList(oauth2Details *oidc.IDToken) ([]Oauth2Customer, error) {
|
|
||||||
var claimsJSON Oauth2Claims
|
|
||||||
if err := oauth2Details.Claims(&claimsJSON); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
customersList := make([]Oauth2Customer, 0, len(claimsJSON.CAGroups))
|
|
||||||
for _, v := range claimsJSON.CAGroups {
|
|
||||||
var caCustomer Oauth2Customer
|
|
||||||
if err := json.Unmarshal([]byte(v), &caCustomer); err == nil {
|
|
||||||
customersList = append(customersList, caCustomer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return customersList, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// func MakeAuthCookies(custGUID uuid.UUID, ouToken *oidc.IDToken) (*http.Cookie, error) {
|
|
||||||
// var ccc http.Cookie
|
|
||||||
// var responseData AuthenticationCookie
|
|
||||||
// expireDate := time.Now().UTC().Add(time.Duration(config.CookieExpirationHours) * time.Hour)
|
|
||||||
// if ouToken != nil {
|
|
||||||
// expireDate = ouToken.Expiry
|
|
||||||
// }
|
|
||||||
// ccc.Expires = expireDate
|
|
||||||
// responseData.CustomerGUID = custGUID
|
|
||||||
// responseData.Expires = ccc.Expires
|
|
||||||
// responseData.Version = 0
|
|
||||||
// authorizationStr := ""
|
|
||||||
// if ouToken != nil {
|
|
||||||
// authorizationStr = ouToken.Nonce
|
|
||||||
// if err := ouToken.Claims(&responseData.Oauth2Claims); err != nil {
|
|
||||||
// errStr := fmt.Sprintf("failed to get claims from JWT")
|
|
||||||
// return nil, fmt.Errorf("%v", errStr)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// jsonBytes, err := json.Marshal(responseData)
|
|
||||||
// if err != nil {
|
|
||||||
// errStr := fmt.Sprintf("failed to get claims from JWT")
|
|
||||||
// return nil, fmt.Errorf("%v", errStr)
|
|
||||||
// }
|
|
||||||
// ccc.Name = "auth"
|
|
||||||
// ccc.Value = hex.EncodeToString(jsonBytes) + "." + cacheaccess.CalcHmac256(jsonBytes)
|
|
||||||
// // TODO: HttpOnly for security...
|
|
||||||
// ccc.HttpOnly = false
|
|
||||||
// ccc.Path = "/"
|
|
||||||
// ccc.Secure = true
|
|
||||||
// ccc.SameSite = http.SameSiteNoneMode
|
|
||||||
// http.SetCookie(w, &ccc)
|
|
||||||
// responseData.Authorization = authorizationStr
|
|
||||||
// jsonBytes, err = json.Marshal(responseData)
|
|
||||||
// if err != nil {
|
|
||||||
// w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
// fmt.Fprintf(w, "error while marshaling response(2) %s", err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// w.Write(jsonBytes)
|
|
||||||
// }
|
|
||||||
|
|
||||||
func Login(loginDetails CustomerLoginDetails) (*LoginObject, error) {
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetBEInfo(cfgFile string) string {
|
|
||||||
return "https://dashbe.eudev3.cyberarmorsoft.com"
|
|
||||||
}
|
|
||||||
|
|
||||||
func BELogin(loginDetails *CustomerLoginDetails, login string, cfg string) (*BELoginResponse, error) {
|
|
||||||
client := &http.Client{}
|
|
||||||
|
|
||||||
basebeURL := GetBEInfo(cfg)
|
|
||||||
beURL := fmt.Sprintf("%v/%v", basebeURL, login)
|
|
||||||
|
|
||||||
loginInfoBytes, err := json.Marshal(loginDetails)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("POST", beURL, bytes.NewReader(loginInfoBytes))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Referer", strings.Replace(beURL, "dashbe", "cpanel", 1))
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
loginS := &BELoginResponse{}
|
|
||||||
json.Unmarshal(body, &loginS)
|
|
||||||
|
|
||||||
loginS.Cookies = resp.Cookies()
|
|
||||||
return loginS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *LoginObject) IsExpired() bool {
|
|
||||||
if r == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
t, err := time.Parse(time.RFC3339, r.Expires)
|
|
||||||
if err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return t.UTC().Before(time.Now().UTC())
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
// func TestLogin2BE(t *testing.T) {
|
|
||||||
|
|
||||||
// loginDetails := CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "***", CustomerName: "CyberArmorTests"}
|
|
||||||
// res, err := BELogin(loginDetails, "login")
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("failed to get raw audit is different ")
|
|
||||||
// }
|
|
||||||
// k := res.ToLoginObject()
|
|
||||||
|
|
||||||
// fmt.Printf("%v\n", k)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestGetMicroserviceOverview(t *testing.T) {
|
|
||||||
// // client := &http.Client{}
|
|
||||||
// loginDetails := CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "***", CustomerName: "CyberArmorTests"}
|
|
||||||
// loginobj, err := BELogin(loginDetails, "login")
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("failed to get raw audit is different ")
|
|
||||||
// }
|
|
||||||
// k := loginobj.ToLoginObject()
|
|
||||||
// beURL := GetBEInfo("")
|
|
||||||
|
|
||||||
// res, err := BEHttpRequest(k, beURL,
|
|
||||||
// "GET",
|
|
||||||
// "v1/microservicesOverview",
|
|
||||||
// nil,
|
|
||||||
// BasicBEQuery,
|
|
||||||
// k)
|
|
||||||
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("failed to get raw audit is different ")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// s := string(res)
|
|
||||||
|
|
||||||
// fmt.Printf("%v\n", s)
|
|
||||||
|
|
||||||
// }
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// AuthenticationCookie is what it is
|
|
||||||
type AuthenticationCookie struct {
|
|
||||||
Oauth2Claims `json:",inline"`
|
|
||||||
CustomerGUID uuid.UUID `json:"customerGuid"`
|
|
||||||
Expires time.Time `json:"expires"`
|
|
||||||
Version int `json:"version"`
|
|
||||||
Authorization string `json:"authorization,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginAux struct {
|
|
||||||
Referer string
|
|
||||||
Url string
|
|
||||||
}
|
|
||||||
|
|
||||||
// CustomerLoginDetails is what it is
|
|
||||||
type CustomerLoginDetails struct {
|
|
||||||
Email string `json:"email"`
|
|
||||||
Password string `json:"password"`
|
|
||||||
CustomerName string `json:"customer,omitempty"`
|
|
||||||
CustomerGUID uuid.UUID `json:"customerGuid,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Oauth2Claims returns in claims section of Oauth2 verification process
|
|
||||||
type Oauth2Claims struct {
|
|
||||||
Sub string `json:"sub"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
PreferredUserName string `json:"preferred_username"`
|
|
||||||
CAGroups []string `json:"ca_groups"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package apis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Commands list of commands received from websocket
|
|
||||||
type Commands struct {
|
|
||||||
Commands []Command `json:"commands"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command structure of command received from websocket
|
|
||||||
type Command struct {
|
|
||||||
CommandName string `json:"commandName"`
|
|
||||||
ResponseID string `json:"responseID"`
|
|
||||||
Wlid string `json:"wlid,omitempty"`
|
|
||||||
WildWlid string `json:"wildWlid,omitempty"`
|
|
||||||
Sid string `json:"sid,omitempty"`
|
|
||||||
WildSid string `json:"wildSid,omitempty"`
|
|
||||||
JobTracking JobTracking `json:"jobTracking"`
|
|
||||||
Args map[string]interface{} `json:"args,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type JobTracking struct {
|
|
||||||
JobID string `json:"jobID,omitempty"`
|
|
||||||
ParentID string `json:"parentAction,omitempty"`
|
|
||||||
LastActionNumber int `json:"numSeq,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) DeepCopy() *Command {
|
|
||||||
newCommand := &Command{}
|
|
||||||
newCommand.CommandName = c.CommandName
|
|
||||||
newCommand.ResponseID = c.ResponseID
|
|
||||||
newCommand.Wlid = c.Wlid
|
|
||||||
newCommand.WildWlid = c.WildWlid
|
|
||||||
if c.Args != nil {
|
|
||||||
newCommand.Args = make(map[string]interface{})
|
|
||||||
for i, j := range c.Args {
|
|
||||||
newCommand.Args[i] = j
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) GetLabels() map[string]string {
|
|
||||||
if c.Args != nil {
|
|
||||||
if ilabels, ok := c.Args["labels"]; ok {
|
|
||||||
labels := map[string]string{}
|
|
||||||
if b, e := json.Marshal(ilabels); e == nil {
|
|
||||||
if e = json.Unmarshal(b, &labels); e == nil {
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map[string]string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) SetLabels(labels map[string]string) {
|
|
||||||
if c.Args == nil {
|
|
||||||
c.Args = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
c.Args["labels"] = labels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) GetFieldSelector() map[string]string {
|
|
||||||
if c.Args != nil {
|
|
||||||
if ilabels, ok := c.Args["fieldSelector"]; ok {
|
|
||||||
labels := map[string]string{}
|
|
||||||
if b, e := json.Marshal(ilabels); e == nil {
|
|
||||||
if e = json.Unmarshal(b, &labels); e == nil {
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map[string]string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) SetFieldSelector(labels map[string]string) {
|
|
||||||
if c.Args == nil {
|
|
||||||
c.Args = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
c.Args["fieldSelector"] = labels
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) GetID() string {
|
|
||||||
if c.WildWlid != "" {
|
|
||||||
return c.WildWlid
|
|
||||||
}
|
|
||||||
if c.WildSid != "" {
|
|
||||||
return c.WildSid
|
|
||||||
}
|
|
||||||
if c.Wlid != "" {
|
|
||||||
return c.Wlid
|
|
||||||
}
|
|
||||||
if c.Sid != "" {
|
|
||||||
return c.Sid
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Command) Json() string {
|
|
||||||
b, _ := json.Marshal(*c)
|
|
||||||
return fmt.Sprintf("%s", b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func SIDFallback(c *Command) {
|
|
||||||
if c.GetID() == "" {
|
|
||||||
sid, err := getSIDFromArgs(c.Args)
|
|
||||||
if err != nil || sid == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Sid = sid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSIDFromArgs(args map[string]interface{}) (string, error) {
|
|
||||||
sidInterface, ok := args["sid"]
|
|
||||||
if !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
sid, ok := sidInterface.(string)
|
|
||||||
if !ok || sid == "" {
|
|
||||||
return "", fmt.Errorf("sid found in args but empty")
|
|
||||||
}
|
|
||||||
// if _, err := secrethandling.SplitSecretID(sid); err != nil {
|
|
||||||
// return "", err
|
|
||||||
// }
|
|
||||||
return sid, nil
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
|
|
||||||
type EnforcementsRule struct {
|
|
||||||
MonitoredObject []string `json:"monitoredObject"`
|
|
||||||
MonitoredObjectExistence []string `json:"objectExistence"`
|
|
||||||
MonitoredObjectEvent []string `json:"event"`
|
|
||||||
Action []string `json:"action"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExecutionPolicy struct {
|
|
||||||
PortalBase `json:",inline"`
|
|
||||||
Designators []PortalDesignator `json:"designators"`
|
|
||||||
PolicyType string `json:"policyType"`
|
|
||||||
CreationTime string `json:"creation_time"`
|
|
||||||
ExecutionEnforcementsRule []EnforcementsRule `json:"enforcementRules"`
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
|
|
||||||
const (
|
|
||||||
CostumerGuidQuery = "costumerGUID"
|
|
||||||
ClusterNameQuery = "cluster"
|
|
||||||
DatacenterNameQuery = "datacenter"
|
|
||||||
NamespaceQuery = "namespace"
|
|
||||||
ProjectQuery = "project"
|
|
||||||
WlidQuery = "wlid"
|
|
||||||
SidQuery = "sid"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PortalBase holds basic items data from portal BE
|
|
||||||
type PortalBase struct {
|
|
||||||
GUID string `json:"guid"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Attributes map[string]interface{} `json:"attributes,omitempty"` // could be string
|
|
||||||
}
|
|
||||||
|
|
||||||
type DesignatorType string
|
|
||||||
|
|
||||||
// Supported designators
|
|
||||||
const (
|
|
||||||
DesignatorAttributes DesignatorType = "Attributes"
|
|
||||||
DesignatorAttribute DesignatorType = "Attribute" // Deprecated
|
|
||||||
/*
|
|
||||||
WorkloadID format.
|
|
||||||
k8s format: wlid://cluster-<cluster>/namespace-<namespace>/<kind>-<name>
|
|
||||||
native format: wlid://datacenter-<datacenter>/project-<project>/native-<name>
|
|
||||||
*/
|
|
||||||
DesignatorWlid DesignatorType = "Wlid"
|
|
||||||
/*
|
|
||||||
Wild card - subset of wlid. e.g.
|
|
||||||
1. Include cluster:
|
|
||||||
wlid://cluster-<cluster>/
|
|
||||||
2. Include cluster and namespace (filter out all other namespaces):
|
|
||||||
wlid://cluster-<cluster>/namespace-<namespace>/
|
|
||||||
*/
|
|
||||||
DesignatorWildWlid DesignatorType = "WildWlid"
|
|
||||||
DesignatorWlidContainer DesignatorType = "WlidContainer"
|
|
||||||
DesignatorWlidProcess DesignatorType = "WlidProcess"
|
|
||||||
DesignatorSid DesignatorType = "Sid" // secret id
|
|
||||||
)
|
|
||||||
|
|
||||||
// attributes
|
|
||||||
const (
|
|
||||||
AttributeCluster = "cluster"
|
|
||||||
AttributeNamespace = "namespace"
|
|
||||||
AttributeKind = "kind"
|
|
||||||
AttributeName = "name"
|
|
||||||
)
|
|
||||||
|
|
||||||
// PortalDesignator represented single designation options
|
|
||||||
type PortalDesignator struct {
|
|
||||||
DesignatorType DesignatorType `json:"designatorType"`
|
|
||||||
WLID string `json:"wlid"`
|
|
||||||
WildWLID string `json:"wildwlid"`
|
|
||||||
SID string `json:"sid"`
|
|
||||||
Attributes map[string]string `json:"attributes"`
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
|
|
||||||
func MockPortalBase(customerGUID, name string, attributes map[string]interface{}) *PortalBase {
|
|
||||||
if customerGUID == "" {
|
|
||||||
customerGUID = "36b6f9e1-3b63-4628-994d-cbe16f81e9c7"
|
|
||||||
}
|
|
||||||
if name == "" {
|
|
||||||
name = "portalbase-a"
|
|
||||||
}
|
|
||||||
if attributes == nil {
|
|
||||||
attributes = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
return &PortalBase{
|
|
||||||
GUID: customerGUID,
|
|
||||||
Name: name,
|
|
||||||
Attributes: attributes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/armosec/kubescape/cautils/cautils"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
var IgnoreLabels = []string{AttributeCluster, AttributeNamespace}
|
|
||||||
|
|
||||||
func (designator *PortalDesignator) GetCluster() string {
|
|
||||||
cluster, _, _, _, _ := designator.DigestPortalDesignator()
|
|
||||||
return cluster
|
|
||||||
}
|
|
||||||
|
|
||||||
func (designator *PortalDesignator) GetNamespace() string {
|
|
||||||
_, namespace, _, _, _ := designator.DigestPortalDesignator()
|
|
||||||
return namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
func (designator *PortalDesignator) GetKind() string {
|
|
||||||
_, _, kind, _, _ := designator.DigestPortalDesignator()
|
|
||||||
return kind
|
|
||||||
}
|
|
||||||
|
|
||||||
func (designator *PortalDesignator) GetName() string {
|
|
||||||
_, _, _, name, _ := designator.DigestPortalDesignator()
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
func (designator *PortalDesignator) GetLabels() map[string]string {
|
|
||||||
_, _, _, _, labels := designator.DigestPortalDesignator()
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DigestPortalDesignator - get cluster namespace and labels from designator
|
|
||||||
func (designator *PortalDesignator) DigestPortalDesignator() (string, string, string, string, map[string]string) {
|
|
||||||
switch designator.DesignatorType {
|
|
||||||
case DesignatorAttributes, DesignatorAttribute:
|
|
||||||
return designator.DigestAttributesDesignator()
|
|
||||||
case DesignatorWlid, DesignatorWildWlid:
|
|
||||||
return cautils.GetClusterFromWlid(designator.WLID), cautils.GetNamespaceFromWlid(designator.WLID), cautils.GetKindFromWlid(designator.WLID), cautils.GetNameFromWlid(designator.WLID), map[string]string{}
|
|
||||||
// case DesignatorSid: // TODO
|
|
||||||
default:
|
|
||||||
glog.Warningf("in 'digestPortalDesignator' designator type: '%v' not yet supported. please contact Armo team", designator.DesignatorType)
|
|
||||||
}
|
|
||||||
return "", "", "", "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (designator *PortalDesignator) DigestAttributesDesignator() (string, string, string, string, map[string]string) {
|
|
||||||
cluster := ""
|
|
||||||
namespace := ""
|
|
||||||
kind := ""
|
|
||||||
name := ""
|
|
||||||
labels := map[string]string{}
|
|
||||||
attributes := designator.Attributes
|
|
||||||
if attributes == nil {
|
|
||||||
return cluster, namespace, kind, name, labels
|
|
||||||
}
|
|
||||||
for k, v := range attributes {
|
|
||||||
labels[k] = v
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeNamespace]; ok {
|
|
||||||
namespace = v
|
|
||||||
delete(labels, AttributeNamespace)
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeCluster]; ok {
|
|
||||||
cluster = v
|
|
||||||
delete(labels, AttributeCluster)
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeKind]; ok {
|
|
||||||
kind = v
|
|
||||||
delete(labels, AttributeKind)
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeName]; ok {
|
|
||||||
name = v
|
|
||||||
delete(labels, AttributeName)
|
|
||||||
}
|
|
||||||
return cluster, namespace, kind, name, labels
|
|
||||||
}
|
|
||||||
|
|
||||||
// DigestPortalDesignator DEPRECATED. use designator.DigestPortalDesignator() - get cluster namespace and labels from designator
|
|
||||||
func DigestPortalDesignator(designator *PortalDesignator) (string, string, map[string]string) {
|
|
||||||
switch designator.DesignatorType {
|
|
||||||
case DesignatorAttributes, DesignatorAttribute:
|
|
||||||
return DigestAttributesDesignator(designator.Attributes)
|
|
||||||
case DesignatorWlid, DesignatorWildWlid:
|
|
||||||
return cautils.GetClusterFromWlid(designator.WLID), cautils.GetNamespaceFromWlid(designator.WLID), map[string]string{}
|
|
||||||
// case DesignatorSid: // TODO
|
|
||||||
default:
|
|
||||||
glog.Warningf("in 'digestPortalDesignator' designator type: '%v' not yet supported. please contact Armo team", designator.DesignatorType)
|
|
||||||
}
|
|
||||||
return "", "", nil
|
|
||||||
}
|
|
||||||
func DigestAttributesDesignator(attributes map[string]string) (string, string, map[string]string) {
|
|
||||||
cluster := ""
|
|
||||||
namespace := ""
|
|
||||||
labels := map[string]string{}
|
|
||||||
if attributes == nil {
|
|
||||||
return cluster, namespace, labels
|
|
||||||
}
|
|
||||||
for k, v := range attributes {
|
|
||||||
labels[k] = v
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeNamespace]; ok {
|
|
||||||
namespace = v
|
|
||||||
delete(labels, AttributeNamespace)
|
|
||||||
}
|
|
||||||
if v, ok := attributes[AttributeCluster]; ok {
|
|
||||||
cluster = v
|
|
||||||
delete(labels, AttributeCluster)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cluster, namespace, labels
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
|
|
||||||
type PostureExceptionPolicyActions string
|
|
||||||
|
|
||||||
const AlertOnly PostureExceptionPolicyActions = "alertOnly"
|
|
||||||
const Disable PostureExceptionPolicyActions = "disable"
|
|
||||||
|
|
||||||
type PostureExceptionPolicy struct {
|
|
||||||
PortalBase `json:",inline"`
|
|
||||||
PolicyType string `json:"policyType"`
|
|
||||||
CreationTime string `json:"creationTime"`
|
|
||||||
Actions []PostureExceptionPolicyActions `json:"actions"`
|
|
||||||
Resources []PortalDesignator `json:"resources"`
|
|
||||||
PosturePolicies []PosturePolicy `json:"posturePolicies"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PosturePolicy struct {
|
|
||||||
FrameworkName string `json:"frameworkName"`
|
|
||||||
ControlName string `json:"controlName"`
|
|
||||||
RuleName string `json:"ruleName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (exceptionPolicy *PostureExceptionPolicy) IsAlertOnly() bool {
|
|
||||||
if exceptionPolicy.IsDisable() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range exceptionPolicy.Actions {
|
|
||||||
if exceptionPolicy.Actions[i] == AlertOnly {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
func (exceptionPolicy *PostureExceptionPolicy) IsDisable() bool {
|
|
||||||
for i := range exceptionPolicy.Actions {
|
|
||||||
if exceptionPolicy.Actions[i] == Disable {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
package armotypes
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// labels added to the workload
|
|
||||||
const (
|
|
||||||
ArmoPrefix string = "armo"
|
|
||||||
ArmoAttach string = ArmoPrefix + ".attach"
|
|
||||||
ArmoInitialSecret string = ArmoPrefix + ".initial"
|
|
||||||
ArmoSecretStatus string = ArmoPrefix + ".secret"
|
|
||||||
ArmoCompatibleLabel string = ArmoPrefix + ".compatible"
|
|
||||||
|
|
||||||
ArmoSecretProtectStatus string = "protect"
|
|
||||||
ArmoSecretClearStatus string = "clear"
|
|
||||||
)
|
|
||||||
|
|
||||||
// annotations added to the workload
|
|
||||||
const (
|
|
||||||
ArmoUpdate string = ArmoPrefix + ".last-update"
|
|
||||||
ArmoWlid string = ArmoPrefix + ".wlid"
|
|
||||||
ArmoSid string = ArmoPrefix + ".sid"
|
|
||||||
ArmoJobID string = ArmoPrefix + ".job"
|
|
||||||
ArmoJobIDPath string = ArmoJobID + "/id"
|
|
||||||
ArmoJobParentPath string = ArmoJobID + "/parent"
|
|
||||||
ArmoJobActionPath string = ArmoJobID + "/action"
|
|
||||||
ArmoCompatibleAnnotation string = ArmoAttach + "/compatible"
|
|
||||||
ArmoReplaceheaders string = ArmoAttach + "/replaceheaders"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // DEPRECATED
|
|
||||||
|
|
||||||
CAAttachLabel string = "cyberarmor"
|
|
||||||
Patched string = "Patched"
|
|
||||||
Done string = "Done"
|
|
||||||
Encrypted string = "Protected"
|
|
||||||
|
|
||||||
CAInjectOld = "injectCyberArmor"
|
|
||||||
|
|
||||||
CAPrefix string = "cyberarmor"
|
|
||||||
CAProtectedSecret string = CAPrefix + ".secret"
|
|
||||||
CAInitialSecret string = CAPrefix + ".initial"
|
|
||||||
CAInject string = CAPrefix + ".inject"
|
|
||||||
CAIgnore string = CAPrefix + ".ignore"
|
|
||||||
CAReplaceHeaders string = CAPrefix + ".removeSecurityHeaders"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ( // DEPRECATED
|
|
||||||
CAUpdate string = CAPrefix + ".last-update"
|
|
||||||
CAStatus string = CAPrefix + ".status"
|
|
||||||
CAWlid string = CAPrefix + ".wlid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ClusterConfig struct {
|
|
||||||
EventReceiverREST string `json:"eventReceiverREST"`
|
|
||||||
EventReceiverWS string `json:"eventReceiverWS"`
|
|
||||||
MaserNotificationServer string `json:"maserNotificationServer"`
|
|
||||||
Postman string `json:"postman"`
|
|
||||||
Dashboard string `json:"dashboard"`
|
|
||||||
Portal string `json:"portal"`
|
|
||||||
CustomerGUID string `json:"customerGUID"`
|
|
||||||
ClusterGUID string `json:"clusterGUID"`
|
|
||||||
ClusterName string `json:"clusterName"`
|
|
||||||
OciImageURL string `json:"ociImageURL"`
|
|
||||||
NotificationWSURL string `json:"notificationWSURL"`
|
|
||||||
NotificationRestURL string `json:"notificationRestURL"`
|
|
||||||
VulnScanURL string `json:"vulnScanURL"`
|
|
||||||
OracleURL string `json:"oracleURL"`
|
|
||||||
ClairURL string `json:"clairURL"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// represents workload basic info
|
|
||||||
type SpiffeBasicInfo struct {
|
|
||||||
//cluster/datacenter
|
|
||||||
Level0 string `json:"level0"`
|
|
||||||
Level0Type string `json:"level0Type"`
|
|
||||||
|
|
||||||
//namespace/project
|
|
||||||
Level1 string `json:"level0"`
|
|
||||||
Level1Type string `json:"level0Type"`
|
|
||||||
|
|
||||||
Kind string `json:"kind"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ImageInfo struct {
|
|
||||||
Registry string `json:"registry"`
|
|
||||||
VersionImage string `json:"versionImage"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsAttached(labels map[string]string) *bool {
|
|
||||||
attach := false
|
|
||||||
if labels == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if attached, ok := labels[ArmoAttach]; ok {
|
|
||||||
if strings.ToLower(attached) == "true" {
|
|
||||||
attach = true
|
|
||||||
return &attach
|
|
||||||
} else {
|
|
||||||
return &attach
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
if _, ok := labels[CAAttachLabel]; ok {
|
|
||||||
attach = true
|
|
||||||
return &attach
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
if inject, ok := labels[CAInject]; ok {
|
|
||||||
if strings.ToLower(inject) == "true" {
|
|
||||||
attach = true
|
|
||||||
return &attach
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprecated
|
|
||||||
if ignore, ok := labels[CAIgnore]; ok {
|
|
||||||
if strings.ToLower(ignore) == "true" {
|
|
||||||
return &attach
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSecretProtected(labels map[string]string) *bool {
|
|
||||||
protect := false
|
|
||||||
if labels == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if protected, ok := labels[ArmoSecretStatus]; ok {
|
|
||||||
if strings.ToLower(protected) == ArmoSecretProtectStatus {
|
|
||||||
protect = true
|
|
||||||
return &protect
|
|
||||||
} else {
|
|
||||||
return &protect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadConfig(configPath string, loadToEnv bool) (*ClusterConfig, error) {
|
|
||||||
if configPath == "" {
|
|
||||||
configPath = "/etc/config/clusterData.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
dat, err := ioutil.ReadFile(configPath)
|
|
||||||
if err != nil || len(dat) == 0 {
|
|
||||||
return nil, fmt.Errorf("Config empty or not found. path: %s", configPath)
|
|
||||||
}
|
|
||||||
componentConfig := &ClusterConfig{}
|
|
||||||
if err := json.Unmarshal(dat, componentConfig); err != nil {
|
|
||||||
return componentConfig, fmt.Errorf("Failed to read component config, path: %s, reason: %s", configPath, err.Error())
|
|
||||||
}
|
|
||||||
if loadToEnv {
|
|
||||||
componentConfig.LoadConfigToEnv()
|
|
||||||
}
|
|
||||||
return componentConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (clusterConfig *ClusterConfig) LoadConfigToEnv() {
|
|
||||||
|
|
||||||
SetEnv("CA_CLUSTER_NAME", clusterConfig.ClusterName)
|
|
||||||
SetEnv("CA_CLUSTER_GUID", clusterConfig.ClusterGUID)
|
|
||||||
SetEnv("CA_ORACLE_SERVER", clusterConfig.OracleURL)
|
|
||||||
SetEnv("CA_CUSTOMER_GUID", clusterConfig.CustomerGUID)
|
|
||||||
SetEnv("CA_DASHBOARD_BACKEND", clusterConfig.Dashboard)
|
|
||||||
SetEnv("CA_NOTIFICATION_SERVER_REST", clusterConfig.NotificationWSURL)
|
|
||||||
SetEnv("CA_NOTIFICATION_SERVER_WS", clusterConfig.NotificationWSURL)
|
|
||||||
SetEnv("CA_NOTIFICATION_SERVER_REST", clusterConfig.NotificationRestURL)
|
|
||||||
SetEnv("CA_OCIMAGE_URL", clusterConfig.OciImageURL)
|
|
||||||
SetEnv("CA_K8S_REPORT_URL", clusterConfig.EventReceiverWS)
|
|
||||||
SetEnv("CA_EVENT_RECEIVER_HTTP", clusterConfig.EventReceiverREST)
|
|
||||||
SetEnv("CA_VULNSCAN", clusterConfig.VulnScanURL)
|
|
||||||
SetEnv("CA_POSTMAN", clusterConfig.Postman)
|
|
||||||
SetEnv("MASTER_NOTIFICATION_SERVER_HOST", clusterConfig.MaserNotificationServer)
|
|
||||||
SetEnv("CLAIR_URL", clusterConfig.ClairURL)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetEnv(key, value string) {
|
|
||||||
if e := os.Getenv(key); e == "" {
|
|
||||||
if err := os.Setenv(key, value); err != nil {
|
|
||||||
glog.Warning("%s: %s", key, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// tests wlid parse
|
|
||||||
|
|
||||||
func TestSpiffeWLIDToInfoSuccess(t *testing.T) {
|
|
||||||
|
|
||||||
WLID := "wlid://cluster-HipsterShopCluster2/namespace-prod/deployment-cartservice"
|
|
||||||
ms, er := SpiffeToSpiffeInfo(WLID)
|
|
||||||
|
|
||||||
if er != nil || ms.Level0 != "HipsterShopCluster2" || ms.Level0Type != "cluster" || ms.Level1 != "prod" || ms.Level1Type != "namespace" ||
|
|
||||||
ms.Kind != "deployment" || ms.Name != "cartservice" {
|
|
||||||
t.Errorf("TestSpiffeWLIDToInfoSuccess failed to parse %v", WLID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSpiffeSIDInfoSuccess(t *testing.T) {
|
|
||||||
|
|
||||||
SID := "sid://cluster-HipsterShopCluster2/namespace-dev/secret-caregcred"
|
|
||||||
ms, er := SpiffeToSpiffeInfo(SID)
|
|
||||||
|
|
||||||
if er != nil || ms.Level0 != "HipsterShopCluster2" || ms.Level0Type != "cluster" || ms.Level1 != "dev" || ms.Level1Type != "namespace" ||
|
|
||||||
ms.Kind != "secret" || ms.Name != "caregcred" {
|
|
||||||
t.Errorf("TestSpiffeSIDInfoSuccess failed to parse %v", SID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// wlid/ sid utils
|
|
||||||
const (
|
|
||||||
SpiffePrefix = "://"
|
|
||||||
)
|
|
||||||
|
|
||||||
// wlid/ sid utils
|
|
||||||
const (
|
|
||||||
PackagePath = "vendor/github.com/armosec/capacketsgo"
|
|
||||||
)
|
|
||||||
|
|
||||||
//AsSHA256 takes anything turns it into string :) https://blog.8bitzen.com/posts/22-08-2019-how-to-hash-a-struct-in-go
|
|
||||||
func AsSHA256(v interface{}) string {
|
|
||||||
h := sha256.New()
|
|
||||||
h.Write([]byte(fmt.Sprintf("%v", v)))
|
|
||||||
|
|
||||||
return fmt.Sprintf("%x", h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func SpiffeToSpiffeInfo(spiffe string) (*SpiffeBasicInfo, error) {
|
|
||||||
basicInfo := &SpiffeBasicInfo{}
|
|
||||||
|
|
||||||
pos := strings.Index(spiffe, SpiffePrefix)
|
|
||||||
if pos < 0 {
|
|
||||||
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += len(SpiffePrefix)
|
|
||||||
spiffeNoPrefix := spiffe[pos:]
|
|
||||||
splits := strings.Split(spiffeNoPrefix, "/")
|
|
||||||
if len(splits) < 3 {
|
|
||||||
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
|
|
||||||
}
|
|
||||||
|
|
||||||
p0 := strings.Index(splits[0], "-")
|
|
||||||
p1 := strings.Index(splits[1], "-")
|
|
||||||
p2 := strings.Index(splits[2], "-")
|
|
||||||
if p0 == -1 || p1 == -1 || p2 == -1 {
|
|
||||||
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
|
|
||||||
}
|
|
||||||
basicInfo.Level0Type = splits[0][:p0]
|
|
||||||
basicInfo.Level0 = splits[0][p0+1:]
|
|
||||||
basicInfo.Level1Type = splits[1][:p1]
|
|
||||||
basicInfo.Level1 = splits[1][p1+1:]
|
|
||||||
basicInfo.Kind = splits[2][:p2]
|
|
||||||
basicInfo.Name = splits[2][p2+1:]
|
|
||||||
|
|
||||||
return basicInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImageTagToImageInfo(imageTag string) (*ImageInfo, error) {
|
|
||||||
ImageInfo := &ImageInfo{}
|
|
||||||
spDelimiter := "/"
|
|
||||||
pos := strings.Index(imageTag, spDelimiter)
|
|
||||||
if pos < 0 {
|
|
||||||
ImageInfo.Registry = ""
|
|
||||||
ImageInfo.VersionImage = imageTag
|
|
||||||
return ImageInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
splits := strings.Split(imageTag, spDelimiter)
|
|
||||||
if len(splits) == 0 {
|
|
||||||
|
|
||||||
return nil, fmt.Errorf("Invalid image info %s", imageTag)
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageInfo.Registry = splits[0]
|
|
||||||
if len(splits) > 1 {
|
|
||||||
ImageInfo.VersionImage = splits[len(splits)-1]
|
|
||||||
} else {
|
|
||||||
ImageInfo.VersionImage = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return ImageInfo, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func BoolPointer(b bool) *bool { return &b }
|
|
||||||
|
|
||||||
func BoolToString(b bool) string {
|
|
||||||
if b {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func BoolPointerToString(b *bool) string {
|
|
||||||
if b == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
if *b {
|
|
||||||
return "true"
|
|
||||||
}
|
|
||||||
return "false"
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringToBool(s string) bool {
|
|
||||||
if strings.ToLower(s) == "true" || strings.ToLower(s) == "1" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringToBoolPointer(s string) *bool {
|
|
||||||
if strings.ToLower(s) == "true" || strings.ToLower(s) == "1" {
|
|
||||||
return BoolPointer(true)
|
|
||||||
}
|
|
||||||
if strings.ToLower(s) == "false" || strings.ToLower(s) == "0" {
|
|
||||||
return BoolPointer(false)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"hash/fnv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var NamespacesListToIgnore = make([]string, 0)
|
|
||||||
var KubeNamespaces = []string{metav1.NamespaceSystem, metav1.NamespacePublic}
|
|
||||||
|
|
||||||
// NamespacesListToIgnore namespaces to ignore if a pod
|
|
||||||
func InitNamespacesListToIgnore(caNamespace string) {
|
|
||||||
if len(NamespacesListToIgnore) > 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
NamespacesListToIgnore = append(NamespacesListToIgnore, KubeNamespaces...)
|
|
||||||
NamespacesListToIgnore = append(NamespacesListToIgnore, caNamespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IfIgnoreNamespace(ns string) bool {
|
|
||||||
for i := range NamespacesListToIgnore {
|
|
||||||
if NamespacesListToIgnore[i] == ns {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func IfKubeNamespace(ns string) bool {
|
|
||||||
for i := range KubeNamespaces {
|
|
||||||
if NamespacesListToIgnore[i] == ns {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func hash(s string) string {
|
|
||||||
h := fnv.New32a()
|
|
||||||
h.Write([]byte(s))
|
|
||||||
return fmt.Sprintf("%d", h.Sum32())
|
|
||||||
}
|
|
||||||
func GenarateConfigMapName(wlid string) string {
|
|
||||||
name := strings.ToLower(fmt.Sprintf("ca-%s-%s-%s", GetNamespaceFromWlid(wlid), GetKindFromWlid(wlid), GetNameFromWlid(wlid)))
|
|
||||||
if len(name) >= 63 {
|
|
||||||
name = hash(name)
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
@@ -1,238 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// API fields
|
|
||||||
var (
|
|
||||||
WlidPrefix = "wlid://"
|
|
||||||
SidPrefix = "sid://"
|
|
||||||
ClusterWlidPrefix = "cluster-"
|
|
||||||
NamespaceWlidPrefix = "namespace-"
|
|
||||||
DataCenterWlidPrefix = "datacenter-"
|
|
||||||
ProjectWlidPrefix = "project-"
|
|
||||||
SecretSIDPrefix = "secret-"
|
|
||||||
SubSecretSIDPrefix = "subsecret-"
|
|
||||||
K8SKindsList = []string{"ComponentStatus", "ConfigMap", "ControllerRevision", "CronJob",
|
|
||||||
"CustomResourceDefinition", "DaemonSet", "Deployment", "Endpoints", "Event", "HorizontalPodAutoscaler",
|
|
||||||
"Ingress", "Job", "Lease", "LimitRange", "LocalSubjectAccessReview", "MutatingWebhookConfiguration",
|
|
||||||
"Namespace", "NetworkPolicy", "Node", "PersistentVolume", "PersistentVolumeClaim", "Pod",
|
|
||||||
"PodDisruptionBudget", "PodSecurityPolicy", "PodTemplate", "PriorityClass", "ReplicaSet",
|
|
||||||
"ReplicationController", "ResourceQuota", "Role", "RoleBinding", "Secret", "SelfSubjectAccessReview",
|
|
||||||
"SelfSubjectRulesReview", "Service", "ServiceAccount", "StatefulSet", "StorageClass",
|
|
||||||
"SubjectAccessReview", "TokenReview", "ValidatingWebhookConfiguration", "VolumeAttachment"}
|
|
||||||
NativeKindsList = []string{"Dockerized", "Native"}
|
|
||||||
KindReverseMap = map[string]string{}
|
|
||||||
dataImagesList = []string{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsWlid(id string) bool {
|
|
||||||
return strings.HasPrefix(id, WlidPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSid(id string) bool {
|
|
||||||
return strings.HasPrefix(id, SidPrefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetK8SKindFronList get the calculated wlid
|
|
||||||
func GetK8SKindFronList(kind string) string { // TODO GetK8SKindFromList
|
|
||||||
for i := range K8SKindsList {
|
|
||||||
if strings.ToLower(kind) == strings.ToLower(K8SKindsList[i]) {
|
|
||||||
return K8SKindsList[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return kind
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsK8SKindInList Check if the kind is a known kind
|
|
||||||
func IsK8SKindInList(kind string) bool {
|
|
||||||
for i := range K8SKindsList {
|
|
||||||
if strings.ToLower(kind) == strings.ToLower(K8SKindsList[i]) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateWLID
|
|
||||||
func generateWLID(pLevel0, level0, pLevel1, level1, k, name string) string {
|
|
||||||
kind := strings.ToLower(k)
|
|
||||||
kind = strings.Replace(kind, "-", "", -1)
|
|
||||||
|
|
||||||
wlid := WlidPrefix
|
|
||||||
wlid += fmt.Sprintf("%s%s", pLevel0, level0)
|
|
||||||
if level1 == "" {
|
|
||||||
return wlid
|
|
||||||
}
|
|
||||||
wlid += fmt.Sprintf("/%s%s", pLevel1, level1)
|
|
||||||
|
|
||||||
if kind == "" {
|
|
||||||
return wlid
|
|
||||||
}
|
|
||||||
wlid += fmt.Sprintf("/%s", kind)
|
|
||||||
|
|
||||||
if name == "" {
|
|
||||||
return wlid
|
|
||||||
}
|
|
||||||
wlid += fmt.Sprintf("-%s", name)
|
|
||||||
|
|
||||||
return wlid
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWLID get the calculated wlid
|
|
||||||
func GetWLID(level0, level1, k, name string) string {
|
|
||||||
return generateWLID(ClusterWlidPrefix, level0, NamespaceWlidPrefix, level1, k, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetK8sWLID get the k8s calculated wlid
|
|
||||||
func GetK8sWLID(level0, level1, k, name string) string {
|
|
||||||
return generateWLID(ClusterWlidPrefix, level0, NamespaceWlidPrefix, level1, k, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNativeWLID get the native calculated wlid
|
|
||||||
func GetNativeWLID(level0, level1, k, name string) string {
|
|
||||||
return generateWLID(DataCenterWlidPrefix, level0, ProjectWlidPrefix, level1, k, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WildWlidContainsWlid does WildWlid contains Wlid
|
|
||||||
func WildWlidContainsWlid(wildWlid, wlid string) bool { // TODO- test
|
|
||||||
if wildWlid == wlid {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
wildWlidR, _ := RestoreMicroserviceIDsFromSpiffe(wildWlid)
|
|
||||||
wlidR, _ := RestoreMicroserviceIDsFromSpiffe(wlid)
|
|
||||||
if len(wildWlidR) > len(wildWlidR) {
|
|
||||||
// invalid wlid
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range wildWlidR {
|
|
||||||
if wildWlidR[i] != wlidR[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func restoreInnerIdentifiersFromID(spiffeSlices []string) []string {
|
|
||||||
if len(spiffeSlices) >= 1 && strings.HasPrefix(spiffeSlices[0], ClusterWlidPrefix) {
|
|
||||||
spiffeSlices[0] = spiffeSlices[0][len(ClusterWlidPrefix):]
|
|
||||||
}
|
|
||||||
if len(spiffeSlices) >= 2 && strings.HasPrefix(spiffeSlices[1], NamespaceWlidPrefix) {
|
|
||||||
spiffeSlices[1] = spiffeSlices[1][len(NamespaceWlidPrefix):]
|
|
||||||
}
|
|
||||||
if len(spiffeSlices) >= 3 && strings.Contains(spiffeSlices[2], "-") {
|
|
||||||
dashIdx := strings.Index(spiffeSlices[2], "-")
|
|
||||||
spiffeSlices = append(spiffeSlices, spiffeSlices[2][dashIdx+1:])
|
|
||||||
spiffeSlices[2] = spiffeSlices[2][:dashIdx]
|
|
||||||
if val, ok := KindReverseMap[spiffeSlices[2]]; ok {
|
|
||||||
spiffeSlices[2] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return spiffeSlices
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreMicroserviceIDsFromSpiffe -
|
|
||||||
func RestoreMicroserviceIDsFromSpiffe(spiffe string) ([]string, error) {
|
|
||||||
if spiffe == "" {
|
|
||||||
return nil, fmt.Errorf("in RestoreMicroserviceIDsFromSpiffe, expecting valid wlid recieved empty string")
|
|
||||||
}
|
|
||||||
|
|
||||||
if StringHasWhitespace(spiffe) {
|
|
||||||
return nil, fmt.Errorf("wlid %s invalid. whitespace found", spiffe)
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(spiffe, WlidPrefix) {
|
|
||||||
spiffe = spiffe[len(WlidPrefix):]
|
|
||||||
} else if strings.HasPrefix(spiffe, SidPrefix) {
|
|
||||||
spiffe = spiffe[len(SidPrefix):]
|
|
||||||
}
|
|
||||||
spiffeSlices := strings.Split(spiffe, "/")
|
|
||||||
// The documented WLID format (https://cyberarmorio.sharepoint.com/sites/development2/Shared%20Documents/kubernetes_design1.docx?web=1)
|
|
||||||
if len(spiffeSlices) <= 3 {
|
|
||||||
spiffeSlices = restoreInnerIdentifiersFromID(spiffeSlices)
|
|
||||||
}
|
|
||||||
if len(spiffeSlices) != 4 { // first used WLID, deprecated since 24.10.2019
|
|
||||||
return spiffeSlices, fmt.Errorf("invalid WLID format. format received: %v", spiffeSlices)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range spiffeSlices {
|
|
||||||
if spiffeSlices[i] == "" {
|
|
||||||
return spiffeSlices, fmt.Errorf("one or more entities are empty, spiffeSlices: %v", spiffeSlices)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return spiffeSlices, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreMicroserviceIDsFromSpiffe -
|
|
||||||
func RestoreMicroserviceIDs(spiffe string) []string {
|
|
||||||
if spiffe == "" {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if StringHasWhitespace(spiffe) {
|
|
||||||
return []string{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(spiffe, WlidPrefix) {
|
|
||||||
spiffe = spiffe[len(WlidPrefix):]
|
|
||||||
} else if strings.HasPrefix(spiffe, SidPrefix) {
|
|
||||||
spiffe = spiffe[len(SidPrefix):]
|
|
||||||
}
|
|
||||||
spiffeSlices := strings.Split(spiffe, "/")
|
|
||||||
|
|
||||||
return restoreInnerIdentifiersFromID(spiffeSlices)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClusterFromWlid parse wlid and get cluster
|
|
||||||
func GetClusterFromWlid(wlid string) string {
|
|
||||||
r := RestoreMicroserviceIDs(wlid)
|
|
||||||
if len(r) >= 1 {
|
|
||||||
return r[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNamespaceFromWlid parse wlid and get Namespace
|
|
||||||
func GetNamespaceFromWlid(wlid string) string {
|
|
||||||
r := RestoreMicroserviceIDs(wlid)
|
|
||||||
if len(r) >= 2 {
|
|
||||||
return r[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetKindFromWlid parse wlid and get kind
|
|
||||||
func GetKindFromWlid(wlid string) string {
|
|
||||||
r := RestoreMicroserviceIDs(wlid)
|
|
||||||
if len(r) >= 3 {
|
|
||||||
return GetK8SKindFronList(r[2])
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNameFromWlid parse wlid and get name
|
|
||||||
func GetNameFromWlid(wlid string) string {
|
|
||||||
r := RestoreMicroserviceIDs(wlid)
|
|
||||||
if len(r) >= 4 {
|
|
||||||
return GetK8SKindFronList(r[3])
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsWlidValid test if wlid is a valid wlid
|
|
||||||
func IsWlidValid(wlid string) error {
|
|
||||||
_, err := RestoreMicroserviceIDsFromSpiffe(wlid)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// StringHasWhitespace check if a string has whitespace
|
|
||||||
func StringHasWhitespace(str string) bool {
|
|
||||||
if whitespace := strings.Index(str, " "); whitespace != -1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// K8SResources map[<api group>/<api version>/<resource>]<resource object>
|
|
||||||
type K8SResources map[string]interface{}
|
|
||||||
|
|
||||||
type OPASessionObj struct {
|
|
||||||
Frameworks []opapolicy.Framework
|
|
||||||
K8SResources *K8SResources
|
|
||||||
Exceptions []armotypes.PostureExceptionPolicy
|
|
||||||
PostureReport *opapolicy.PostureReport
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOPASessionObj(frameworks []opapolicy.Framework, k8sResources *K8SResources) *OPASessionObj {
|
|
||||||
return &OPASessionObj{
|
|
||||||
Frameworks: frameworks,
|
|
||||||
K8SResources: k8sResources,
|
|
||||||
PostureReport: &opapolicy.PostureReport{
|
|
||||||
ClusterName: ClusterName,
|
|
||||||
CustomerGUID: CustomerGUID,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewOPASessionObjMock() *OPASessionObj {
|
|
||||||
return &OPASessionObj{
|
|
||||||
Frameworks: nil,
|
|
||||||
K8SResources: nil,
|
|
||||||
PostureReport: &opapolicy.PostureReport{
|
|
||||||
ClusterName: "",
|
|
||||||
CustomerGUID: "",
|
|
||||||
ReportID: "",
|
|
||||||
JobID: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ComponentConfig struct {
|
|
||||||
Exceptions Exception `json:"exceptions"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Exception struct {
|
|
||||||
Ignore *bool `json:"ignore"` // ignore test results
|
|
||||||
MultipleScore *opapolicy.AlertScore `json:"multipleScore"` // MultipleScore number - float32
|
|
||||||
Namespaces []string `json:"namespaces"`
|
|
||||||
Regex string `json:"regex"` // not supported
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/briandowns/spinner"
|
|
||||||
"github.com/fatih/color"
|
|
||||||
"github.com/mattn/go-isatty"
|
|
||||||
)
|
|
||||||
|
|
||||||
var silent = false
|
|
||||||
|
|
||||||
func SetSilentMode(s bool) {
|
|
||||||
silent = s
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsSilent() bool {
|
|
||||||
return silent
|
|
||||||
}
|
|
||||||
|
|
||||||
var FailureDisplay = color.New(color.Bold, color.FgHiRed).FprintfFunc()
|
|
||||||
var WarningDisplay = color.New(color.Bold, color.FgCyan).FprintfFunc()
|
|
||||||
var FailureTextDisplay = color.New(color.Faint, color.FgHiRed).FprintfFunc()
|
|
||||||
var InfoDisplay = color.New(color.Bold, color.FgHiYellow).FprintfFunc()
|
|
||||||
var InfoTextDisplay = color.New(color.Faint, color.FgHiYellow).FprintfFunc()
|
|
||||||
var SimpleDisplay = color.New(color.Bold, color.FgHiWhite).FprintfFunc()
|
|
||||||
var SuccessDisplay = color.New(color.Bold, color.FgHiGreen).FprintfFunc()
|
|
||||||
var DescriptionDisplay = color.New(color.Faint, color.FgWhite).FprintfFunc()
|
|
||||||
|
|
||||||
var Spinner *spinner.Spinner
|
|
||||||
|
|
||||||
func ScanStartDisplay() {
|
|
||||||
if IsSilent() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
InfoDisplay(os.Stdout, "ARMO security scanner starting\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func SuccessTextDisplay(str string) {
|
|
||||||
if IsSilent() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
SuccessDisplay(os.Stdout, "[success] ")
|
|
||||||
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ErrorDisplay(str string) {
|
|
||||||
if IsSilent() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
SuccessDisplay(os.Stdout, "[Error] ")
|
|
||||||
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ProgressTextDisplay(str string) {
|
|
||||||
if IsSilent() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
InfoDisplay(os.Stdout, "[progress] ")
|
|
||||||
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
|
|
||||||
|
|
||||||
}
|
|
||||||
func StartSpinner() {
|
|
||||||
if !IsSilent() && isatty.IsTerminal(os.Stdout.Fd()) {
|
|
||||||
Spinner = spinner.New(spinner.CharSets[7], 100*time.Millisecond) // Build our new spinner
|
|
||||||
Spinner.Start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func StopSpinner() {
|
|
||||||
if Spinner == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Spinner.Stop()
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
type DownloadInfo struct {
|
|
||||||
Path string
|
|
||||||
FrameworkName string
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
// CA environment vars
|
|
||||||
var (
|
|
||||||
CustomerGUID = ""
|
|
||||||
ClusterName = ""
|
|
||||||
EventReceiverURL = ""
|
|
||||||
NotificationServerURL = ""
|
|
||||||
DashboardBackendURL = ""
|
|
||||||
RestAPIPort = "4001"
|
|
||||||
)
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package getter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// =======================================================================================================================
|
|
||||||
// =============================================== ArmoAPI ===============================================================
|
|
||||||
// =======================================================================================================================
|
|
||||||
|
|
||||||
// Armo API for downloading policies
|
|
||||||
type ArmoAPI struct {
|
|
||||||
httpClient *http.Client
|
|
||||||
baseURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewArmoAPI() *ArmoAPI {
|
|
||||||
return &ArmoAPI{
|
|
||||||
httpClient: &http.Client{},
|
|
||||||
baseURL: "https://dashbe.euprod1.cyberarmorsoft.com",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (armoAPI *ArmoAPI) GetFramework(name string) (*opapolicy.Framework, error) {
|
|
||||||
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
framework := &opapolicy.Framework{}
|
|
||||||
if err = JSONDecoder(respStr).Decode(framework); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
SaveFrameworkInFile(framework, GetDefaultPath(name))
|
|
||||||
|
|
||||||
return framework, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
|
|
||||||
requestURI := "v1/armoFrameworks"
|
|
||||||
requestURI += fmt.Sprintf("?customerGUID=%s", "11111111-1111-1111-1111-111111111111")
|
|
||||||
requestURI += fmt.Sprintf("&frameworkName=%s", strings.ToUpper(frameworkName))
|
|
||||||
requestURI += "&getRules=true"
|
|
||||||
|
|
||||||
return urlEncoder(fmt.Sprintf("%s/%s", armoAPI.baseURL, requestURI))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (armoAPI *ArmoAPI) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
|
||||||
exceptions := []armotypes.PostureExceptionPolicy{}
|
|
||||||
if customerGUID == "" {
|
|
||||||
return exceptions, nil
|
|
||||||
}
|
|
||||||
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getExceptionsURL(customerGUID, clusterName))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = JSONDecoder(respStr).Decode(&exceptions); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return exceptions, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) string {
|
|
||||||
requestURI := "api/v1/armoPostureExceptions"
|
|
||||||
requestURI += fmt.Sprintf("?customerGUID=%s", customerGUID)
|
|
||||||
if clusterName != "" {
|
|
||||||
requestURI += fmt.Sprintf("&clusterName=%s", clusterName)
|
|
||||||
}
|
|
||||||
return urlEncoder(fmt.Sprintf("%s/%s", armoAPI.baseURL, requestURI))
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package getter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// =======================================================================================================================
|
|
||||||
// ======================================== DownloadReleasedPolicy =======================================================
|
|
||||||
// =======================================================================================================================
|
|
||||||
|
|
||||||
// Download released version
|
|
||||||
type DownloadReleasedPolicy struct {
|
|
||||||
hostURL string
|
|
||||||
httpClient *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDownloadReleasedPolicy() *DownloadReleasedPolicy {
|
|
||||||
return &DownloadReleasedPolicy{
|
|
||||||
hostURL: "",
|
|
||||||
httpClient: &http.Client{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (drp *DownloadReleasedPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
|
||||||
return []armotypes.PostureExceptionPolicy{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framework, error) {
|
|
||||||
drp.setURL(name)
|
|
||||||
respStr, err := HttpGetter(drp.httpClient, drp.hostURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
framework := &opapolicy.Framework{}
|
|
||||||
if err = JSONDecoder(respStr).Decode(framework); err != nil {
|
|
||||||
return framework, err
|
|
||||||
}
|
|
||||||
|
|
||||||
SaveFrameworkInFile(framework, GetDefaultPath(name))
|
|
||||||
return framework, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (drp *DownloadReleasedPolicy) setURL(frameworkName string) error {
|
|
||||||
|
|
||||||
latestReleases := "https://api.github.com/repos/armosec/regolibrary/releases/latest"
|
|
||||||
resp, err := http.Get(latestReleases)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestReleases, err.Error())
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode < 200 || 301 < resp.StatusCode {
|
|
||||||
return fmt.Errorf("failed to download file, status code: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to read response body from '%s', reason: %s", latestReleases, err.Error())
|
|
||||||
}
|
|
||||||
var data map[string]interface{}
|
|
||||||
err = json.Unmarshal(body, &data)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestReleases, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if assets, ok := data["assets"].([]interface{}); ok {
|
|
||||||
for i := range assets {
|
|
||||||
if asset, ok := assets[i].(map[string]interface{}); ok {
|
|
||||||
if name, ok := asset["name"].(string); ok {
|
|
||||||
if name == frameworkName {
|
|
||||||
if url, ok := asset["browser_download_url"].(string); ok {
|
|
||||||
drp.hostURL = url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package getter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IPolicyGetter interface {
|
|
||||||
GetFramework(name string) (*opapolicy.Framework, error)
|
|
||||||
GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error)
|
|
||||||
// GetScores(scope, customerName, namespace string) ([]armotypes.PostureExceptionPolicy, error)
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
package getter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetDefaultPath(frameworkName string) string {
|
|
||||||
defaultfilePath := filepath.Join(DefaultLocalStore, frameworkName+".json")
|
|
||||||
if homeDir, err := os.UserHomeDir(); err == nil {
|
|
||||||
defaultfilePath = filepath.Join(homeDir, defaultfilePath)
|
|
||||||
}
|
|
||||||
return defaultfilePath
|
|
||||||
}
|
|
||||||
|
|
||||||
func SaveFrameworkInFile(framework *opapolicy.Framework, path string) error {
|
|
||||||
encodedData, err := json.Marshal(framework)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = os.WriteFile(path, []byte(fmt.Sprintf("%v", string(encodedData))), 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONDecoder returns JSON decoder for given string
|
|
||||||
func JSONDecoder(origin string) *json.Decoder {
|
|
||||||
dec := json.NewDecoder(strings.NewReader(origin))
|
|
||||||
dec.UseNumber()
|
|
||||||
return dec
|
|
||||||
}
|
|
||||||
|
|
||||||
func HttpGetter(httpClient *http.Client, fullURL string) (string, error) {
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", fullURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
resp, err := httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
respStr, err := httpRespToString(resp)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return respStr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRespToString parses the body as string and checks the HTTP status code, it closes the body reader at the end
|
|
||||||
func httpRespToString(resp *http.Response) (string, error) {
|
|
||||||
if resp == nil || resp.Body == nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
strBuilder := strings.Builder{}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.ContentLength > 0 {
|
|
||||||
strBuilder.Grow(int(resp.ContentLength))
|
|
||||||
}
|
|
||||||
bytesNum, err := io.Copy(&strBuilder, resp.Body)
|
|
||||||
respStr := strBuilder.String()
|
|
||||||
if err != nil {
|
|
||||||
respStrNewLen := len(respStr)
|
|
||||||
if respStrNewLen > 1024 {
|
|
||||||
respStrNewLen = 1024
|
|
||||||
}
|
|
||||||
return "", fmt.Errorf("HTTP request failed. URL: '%s', Read-ERROR: '%s', HTTP-CODE: '%s', BODY(top): '%s', HTTP-HEADERS: %v, HTTP-BODY-BUFFER-LENGTH: %v", resp.Request.URL.RequestURI(), err, resp.Status, respStr[:respStrNewLen], resp.Header, bytesNum)
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
respStrNewLen := len(respStr)
|
|
||||||
if respStrNewLen > 1024 {
|
|
||||||
respStrNewLen = 1024
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("HTTP request failed. URL: '%s', HTTP-ERROR: '%s', BODY: '%s', HTTP-HEADERS: %v, HTTP-BODY-BUFFER-LENGTH: %v", resp.Request.URL.RequestURI(), resp.Status, respStr[:respStrNewLen], resp.Header, bytesNum)
|
|
||||||
}
|
|
||||||
|
|
||||||
return respStr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// URLEncoder encode url
|
|
||||||
func urlEncoder(oldURL string) string {
|
|
||||||
fullURL := strings.Split(oldURL, "?")
|
|
||||||
baseURL, err := url.Parse(fullURL[0])
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare Query Parameters
|
|
||||||
if len(fullURL) > 1 {
|
|
||||||
params := url.Values{}
|
|
||||||
queryParams := strings.Split(fullURL[1], "&")
|
|
||||||
for _, i := range queryParams {
|
|
||||||
queryParam := strings.Split(i, "=")
|
|
||||||
val := ""
|
|
||||||
if len(queryParam) > 1 {
|
|
||||||
val = queryParam[1]
|
|
||||||
}
|
|
||||||
params.Add(queryParam[0], val)
|
|
||||||
}
|
|
||||||
baseURL.RawQuery = params.Encode()
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseURL.String()
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
package getter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
// =======================================================================================================================
|
|
||||||
// ============================================== LoadPolicy =============================================================
|
|
||||||
// =======================================================================================================================
|
|
||||||
const DefaultLocalStore = ".kubescape"
|
|
||||||
|
|
||||||
// Load policies from a local repository
|
|
||||||
type LoadPolicy struct {
|
|
||||||
filePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLoadPolicy(filePath string) *LoadPolicy {
|
|
||||||
return &LoadPolicy{
|
|
||||||
filePath: filePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lp *LoadPolicy) GetFramework(frameworkName string) (*opapolicy.Framework, error) {
|
|
||||||
|
|
||||||
framework := &opapolicy.Framework{}
|
|
||||||
f, err := ioutil.ReadFile(lp.filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(f, framework)
|
|
||||||
if frameworkName != "" && !strings.EqualFold(frameworkName, framework.Name) {
|
|
||||||
return nil, fmt.Errorf("framework from file not matching")
|
|
||||||
}
|
|
||||||
return framework, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
|
||||||
|
|
||||||
exception := []armotypes.PostureExceptionPolicy{}
|
|
||||||
f, err := ioutil.ReadFile(lp.filePath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(f, &exception)
|
|
||||||
return exception, err
|
|
||||||
}
|
|
||||||
@@ -1,265 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
|
||||||
"github.com/aws/aws-sdk-go/service/ecr"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
// For GCR there are some permissions one need to assign in order to allow ARMO to pull images:
|
|
||||||
// https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
|
|
||||||
// gcloud iam service-accounts create armo-controller-sa
|
|
||||||
// gcloud projects add-iam-policy-binding <PROJECT_NAME> --role roles/storage.objectViewer --member "serviceAccount:armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com"
|
|
||||||
// gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:<PROJECT_NAME>.svc.id.goog[cyberarmor-system/ca-controller-service-account]" armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com
|
|
||||||
// kubectl annotate serviceaccount --overwrite --namespace cyberarmor-system ca-controller-service-account iam.gke.io/gcp-service-account=armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com
|
|
||||||
|
|
||||||
const (
|
|
||||||
gcrDefaultServiceAccountName = "default"
|
|
||||||
// armoServiceAccountName = "ca-controller-service-account"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
httpClient = http.Client{Timeout: 5 * time.Second}
|
|
||||||
)
|
|
||||||
|
|
||||||
// CheckIsECRImage check if this image is suspected as ECR hosted image
|
|
||||||
func CheckIsECRImage(imageTag string) bool {
|
|
||||||
return strings.Contains(imageTag, "dkr.ecr")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLoginDetailsForECR return user name + password using the default iam-role OR ~/.aws/config of the machine
|
|
||||||
func GetLoginDetailsForECR(imageTag string) (string, string, error) {
|
|
||||||
// imageTag := "015253967648.dkr.ecr.eu-central-1.amazonaws.com/armo:1"
|
|
||||||
imageTagSlices := strings.Split(imageTag, ".")
|
|
||||||
repo := imageTagSlices[0]
|
|
||||||
region := imageTagSlices[3]
|
|
||||||
mySession := session.Must(session.NewSession())
|
|
||||||
ecrClient := ecr.New(mySession, aws.NewConfig().WithRegion(region))
|
|
||||||
input := &ecr.GetAuthorizationTokenInput{
|
|
||||||
RegistryIds: []*string{&repo},
|
|
||||||
}
|
|
||||||
res, err := ecrClient.GetAuthorizationToken(input)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("in PullFromECR, failed to GetAuthorizationToken: %v", err)
|
|
||||||
}
|
|
||||||
res64 := (*res.AuthorizationData[0].AuthorizationToken)
|
|
||||||
resB, err := base64.StdEncoding.DecodeString(res64)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("in PullFromECR, failed to DecodeString: %v", err)
|
|
||||||
}
|
|
||||||
delimiterIdx := bytes.IndexByte(resB, ':')
|
|
||||||
// userName := resB[:delimiterIdx]
|
|
||||||
// resB = resB[delimiterIdx+1:]
|
|
||||||
// resB, err = base64.StdEncoding.DecodeString(string(resB))
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("failed to DecodeString #2: %v\n\n", err)
|
|
||||||
// }
|
|
||||||
return string(resB[:delimiterIdx]), string(resB[delimiterIdx+1:]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckIsACRImage(imageTag string) bool {
|
|
||||||
// atest1.azurecr.io/go-inf:1
|
|
||||||
return strings.Contains(imageTag, ".azurecr.io/")
|
|
||||||
}
|
|
||||||
|
|
||||||
type azureADDResponseJson struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
RefreshToken string `json:"refresh_token"`
|
|
||||||
ExpiresIn string `json:"expires_in"`
|
|
||||||
ExpiresOn string `json:"expires_on"`
|
|
||||||
NotBefore string `json:"not_before"`
|
|
||||||
Resource string `json:"resource"`
|
|
||||||
TokenType string `json:"token_type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAzureAADAccessToken() (string, error) {
|
|
||||||
msi_endpoint, err := url.Parse("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01")
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("creating URL : %v", err)
|
|
||||||
}
|
|
||||||
msi_parameters := url.Values{}
|
|
||||||
msi_parameters.Add("resource", "https://management.azure.com/")
|
|
||||||
msi_parameters.Add("api-version", "2018-02-01")
|
|
||||||
msi_endpoint.RawQuery = msi_parameters.Encode()
|
|
||||||
req, err := http.NewRequest("GET", msi_endpoint.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("creating HTTP request : %v", err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Metadata", "true")
|
|
||||||
|
|
||||||
// Call managed services for Azure resources token endpoint
|
|
||||||
resp, err := httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("calling token endpoint : %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull out response body
|
|
||||||
responseBytes, err := ioutil.ReadAll(resp.Body)
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("reading response body : %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
return "", fmt.Errorf("azure ActiveDirectory AT resp: %v, %v", resp.Status, string(responseBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshall response body into struct
|
|
||||||
var r azureADDResponseJson
|
|
||||||
err = json.Unmarshal(responseBytes, &r)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("unmarshalling the response: %v", err)
|
|
||||||
}
|
|
||||||
return r.AccessToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLoginDetailsForAzurCR return user name + password to use
|
|
||||||
func GetLoginDetailsForAzurCR(imageTag string) (string, string, error) {
|
|
||||||
// imageTag := "atest1.azurecr.io/go-inf:1"
|
|
||||||
imageTagSlices := strings.Split(imageTag, "/")
|
|
||||||
azureIdensAT, err := getAzureAADAccessToken()
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
atMap := make(map[string]interface{})
|
|
||||||
azureIdensATSlices := strings.Split(azureIdensAT, ".")
|
|
||||||
if len(azureIdensATSlices) < 2 {
|
|
||||||
return "", "", fmt.Errorf("len(azureIdensATSlices) < 2")
|
|
||||||
}
|
|
||||||
resB, err := base64.RawStdEncoding.DecodeString(azureIdensATSlices[1])
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("in GetLoginDetailsForAzurCR, failed to DecodeString: %v, %s", err, azureIdensATSlices[1])
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(resB, &atMap); err != nil {
|
|
||||||
return "", "", fmt.Errorf("failed to unmarshal azureIdensAT: %v, %s", err, string(resB))
|
|
||||||
}
|
|
||||||
// excahnging AAD for ACR refresh token
|
|
||||||
refreshToken, err := excahngeAzureAADAccessTokenForACRRefreshToken(imageTagSlices[0], fmt.Sprintf("%v", atMap["tid"]), azureIdensAT)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("failed to excahngeAzureAADAccessTokenForACRRefreshToken: %v, registry: %s, tenantID: %s, azureAADAT: %s", err, imageTagSlices[0], fmt.Sprintf("%v", atMap["tid"]), azureIdensAT)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "00000000-0000-0000-0000-000000000000", refreshToken, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func excahngeAzureAADAccessTokenForACRRefreshToken(registry, tenantID, azureAADAT string) (string, error) {
|
|
||||||
msi_parameters := url.Values{}
|
|
||||||
msi_parameters.Add("service", registry)
|
|
||||||
msi_parameters.Add("grant_type", "access_token")
|
|
||||||
msi_parameters.Add("tenant", tenantID)
|
|
||||||
msi_parameters.Add("access_token", azureAADAT)
|
|
||||||
postBodyStr := msi_parameters.Encode()
|
|
||||||
req, err := http.NewRequest("POST", fmt.Sprintf("https://%v/oauth2/exchange", registry), strings.NewReader(postBodyStr))
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("creating HTTP request : %v", err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Metadata", "true")
|
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
// Call managed services for Azure resources token endpoint
|
|
||||||
resp, err := httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("calling token endpoint : %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pull out response body
|
|
||||||
responseBytes, err := ioutil.ReadAll(resp.Body)
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("reading response body : %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
return "", fmt.Errorf("azure exchange AT resp: %v, %v", resp.Status, string(responseBytes))
|
|
||||||
}
|
|
||||||
resultMap := make(map[string]string)
|
|
||||||
err = json.Unmarshal(responseBytes, &resultMap)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("unmarshalling the response: %v", err)
|
|
||||||
}
|
|
||||||
return resultMap["refresh_token"], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CheckIsGCRImage(imageTag string) bool {
|
|
||||||
// gcr.io/elated-pottery-310110/golang-inf:2
|
|
||||||
return strings.Contains(imageTag, "gcr.io/")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLoginDetailsForGCR return user name + password to use
|
|
||||||
func GetLoginDetailsForGCR(imageTag string) (string, string, error) {
|
|
||||||
msi_endpoint, err := url.Parse(fmt.Sprintf("http://169.254.169.254/computeMetadata/v1/instance/service-accounts/%s/token", gcrDefaultServiceAccountName))
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("creating URL : %v", err)
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("GET", msi_endpoint.String(), nil)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("creating HTTP request : %v", err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Metadata-Flavor", "Google")
|
|
||||||
|
|
||||||
// Call managed services for Azure resources token endpoint
|
|
||||||
resp, err := httpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", fmt.Errorf("calling token endpoint : %v", err)
|
|
||||||
}
|
|
||||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
||||||
return "", "", fmt.Errorf("HTTP Status : %v, make sure the '%s' service account is configured for ARMO pod", resp.Status, gcrDefaultServiceAccountName)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
respMap := make(map[string]interface{})
|
|
||||||
if err := json.NewDecoder(resp.Body).Decode(&respMap); err != nil {
|
|
||||||
return "", "", fmt.Errorf("json Decode : %v", err)
|
|
||||||
}
|
|
||||||
return "oauth2accesstoken", fmt.Sprintf("%v", respMap["access_token"]), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetCloudVendorRegistryCredentials(imageTag string) (map[string]types.AuthConfig, error) {
|
|
||||||
secrets := map[string]types.AuthConfig{}
|
|
||||||
var errRes error
|
|
||||||
if CheckIsACRImage(imageTag) {
|
|
||||||
userName, password, err := GetLoginDetailsForAzurCR(imageTag)
|
|
||||||
if err != nil {
|
|
||||||
errRes = fmt.Errorf("failed to GetLoginDetailsForACR(%s): %v", imageTag, err)
|
|
||||||
} else {
|
|
||||||
secrets[imageTag] = types.AuthConfig{
|
|
||||||
Username: userName,
|
|
||||||
Password: password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if CheckIsECRImage(imageTag) {
|
|
||||||
userName, password, err := GetLoginDetailsForECR(imageTag)
|
|
||||||
if err != nil {
|
|
||||||
errRes = fmt.Errorf("failed to GetLoginDetailsForECR(%s): %v", imageTag, err)
|
|
||||||
} else {
|
|
||||||
secrets[imageTag] = types.AuthConfig{
|
|
||||||
Username: userName,
|
|
||||||
Password: password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if CheckIsGCRImage(imageTag) {
|
|
||||||
userName, password, err := GetLoginDetailsForGCR(imageTag)
|
|
||||||
if err != nil {
|
|
||||||
errRes = fmt.Errorf("failed to GetLoginDetailsForGCR(%s): %v", imageTag, err)
|
|
||||||
} else {
|
|
||||||
secrets[imageTag] = types.AuthConfig{
|
|
||||||
Username: userName,
|
|
||||||
Password: password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return secrets, errRes
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
restclient "k8s.io/client-go/rest"
|
|
||||||
|
|
||||||
// DO NOT REMOVE - load cloud providers auth
|
|
||||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// K8SConfig pointer to k8s config
|
|
||||||
var K8SConfig *restclient.Config
|
|
||||||
|
|
||||||
// KubernetesApi -
|
|
||||||
type KubernetesApi struct {
|
|
||||||
KubernetesClient kubernetes.Interface
|
|
||||||
DynamicClient dynamic.Interface
|
|
||||||
Context context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewKubernetesApi -
|
|
||||||
func NewKubernetesApi() *KubernetesApi {
|
|
||||||
|
|
||||||
kubernetesClient, err := kubernetes.NewForConfig(GetK8sConfig())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to load config file, reason: %s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
dynamicClient, err := dynamic.NewForConfig(K8SConfig)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to load config file, reason: %s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &KubernetesApi{
|
|
||||||
KubernetesClient: kubernetesClient,
|
|
||||||
DynamicClient: dynamicClient,
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunningIncluster whether running in cluster
|
|
||||||
var RunningIncluster bool
|
|
||||||
|
|
||||||
// LoadK8sConfig load config from local file or from cluster
|
|
||||||
func LoadK8sConfig() error {
|
|
||||||
kubeconfig, err := config.GetConfig()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load kubernetes config: %s\n", strings.ReplaceAll(err.Error(), "KUBERNETES_MASTER", "KUBECONFIG"))
|
|
||||||
}
|
|
||||||
if _, err := restclient.InClusterConfig(); err == nil {
|
|
||||||
RunningIncluster = true
|
|
||||||
}
|
|
||||||
K8SConfig = kubeconfig
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetK8sConfig get config. load if not loaded yet
|
|
||||||
func GetK8sConfig() *restclient.Config {
|
|
||||||
if K8SConfig == nil {
|
|
||||||
if err := LoadK8sConfig(); err != nil {
|
|
||||||
// print error
|
|
||||||
fmt.Printf("%s", err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return K8SConfig
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/cautils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetGroupVersionResource(t *testing.T) {
|
|
||||||
wlid := "wlid://cluster-david-v1/namespace-default/deployment-nginx-deployment"
|
|
||||||
r, err := GetGroupVersionResource(cautils.GetKindFromWlid(wlid))
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r.Group != "apps" {
|
|
||||||
t.Errorf("wrong group")
|
|
||||||
}
|
|
||||||
if r.Version != "v1" {
|
|
||||||
t.Errorf("wrong Version")
|
|
||||||
}
|
|
||||||
if r.Resource != "deployments" {
|
|
||||||
t.Errorf("wrong Resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
r2, err := GetGroupVersionResource("NetworkPolicy")
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if r2.Resource != "networkpolicies" {
|
|
||||||
t.Errorf("wrong Resource")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/cautils"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
"k8s.io/client-go/dynamic"
|
|
||||||
//
|
|
||||||
// Uncomment to load all auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth
|
|
||||||
//
|
|
||||||
// Or uncomment to load specific auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) GetWorkloadByWlid(wlid string) (*Workload, error) {
|
|
||||||
return k8sAPI.GetWorkload(cautils.GetNamespaceFromWlid(wlid), cautils.GetKindFromWlid(wlid), cautils.GetNameFromWlid(wlid))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) GetWorkload(namespace, kind, name string) (*Workload, error) {
|
|
||||||
groupVersionResource, err := GetGroupVersionResource(kind)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := k8sAPI.ResourceInterface(&groupVersionResource, namespace).Get(k8sAPI.Context, name, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to GET resource, kind: '%s', namespace: '%s', name: '%s', reason: %s", kind, namespace, name, err.Error())
|
|
||||||
}
|
|
||||||
return NewWorkloadObj(w.Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) ListWorkloads(groupVersionResource *schema.GroupVersionResource, namespace string, podLabels, fieldSelector map[string]string) ([]Workload, error) {
|
|
||||||
listOptions := metav1.ListOptions{}
|
|
||||||
if podLabels != nil && len(podLabels) > 0 {
|
|
||||||
set := labels.Set(podLabels)
|
|
||||||
listOptions.LabelSelector = SelectorToString(set)
|
|
||||||
}
|
|
||||||
if fieldSelector != nil && len(fieldSelector) > 0 {
|
|
||||||
set := labels.Set(fieldSelector)
|
|
||||||
listOptions.FieldSelector = SelectorToString(set)
|
|
||||||
}
|
|
||||||
uList, err := k8sAPI.ResourceInterface(groupVersionResource, namespace).List(k8sAPI.Context, listOptions)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to LIST resources, reason: %s", err.Error())
|
|
||||||
}
|
|
||||||
workloads := make([]Workload, len(uList.Items))
|
|
||||||
for i := range uList.Items {
|
|
||||||
workloads[i] = *NewWorkloadObj(uList.Items[i].Object)
|
|
||||||
}
|
|
||||||
return workloads, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) DeleteWorkloadByWlid(wlid string) error {
|
|
||||||
groupVersionResource, err := GetGroupVersionResource(cautils.GetKindFromWlid(wlid))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = k8sAPI.ResourceInterface(&groupVersionResource, cautils.GetNamespaceFromWlid(wlid)).Delete(k8sAPI.Context, cautils.GetNameFromWlid(wlid), metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to DELETE resource, workloadID: '%s', reason: %s", wlid, err.Error())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) CreateWorkload(workload *Workload) (*Workload, error) {
|
|
||||||
groupVersionResource, err := GetGroupVersionResource(workload.GetKind())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
obj, err := workload.ToUnstructured()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
w, err := k8sAPI.ResourceInterface(&groupVersionResource, workload.GetNamespace()).Create(k8sAPI.Context, obj, metav1.CreateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to CREATE resource, workload: '%s', reason: %s", workload.Json(), err.Error())
|
|
||||||
}
|
|
||||||
return NewWorkloadObj(w.Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) UpdateWorkload(workload *Workload) (*Workload, error) {
|
|
||||||
groupVersionResource, err := GetGroupVersionResource(workload.GetKind())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
obj, err := workload.ToUnstructured()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
w, err := k8sAPI.ResourceInterface(&groupVersionResource, workload.GetNamespace()).Update(k8sAPI.Context, obj, metav1.UpdateOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to UPDATE resource, workload: '%s', reason: %s", workload.Json(), err.Error())
|
|
||||||
}
|
|
||||||
return NewWorkloadObj(w.Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) GetNamespace(ns string) (*Workload, error) {
|
|
||||||
groupVersionResource, err := GetGroupVersionResource("namespace")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
w, err := k8sAPI.DynamicClient.Resource(groupVersionResource).Get(k8sAPI.Context, ns, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to get namespace: '%s', reason: %s", ns, err.Error())
|
|
||||||
}
|
|
||||||
return NewWorkloadObj(w.Object), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) ResourceInterface(resource *schema.GroupVersionResource, namespace string) dynamic.ResourceInterface {
|
|
||||||
if IsNamespaceScope(resource.Group, resource.Resource) {
|
|
||||||
return k8sAPI.DynamicClient.Resource(*resource).Namespace(namespace)
|
|
||||||
}
|
|
||||||
return k8sAPI.DynamicClient.Resource(*resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) CalculateWorkloadParentRecursive(workload *Workload) (string, string, error) {
|
|
||||||
ownerReferences, err := workload.GetOwnerReferences() // OwnerReferences in workload
|
|
||||||
if err != nil {
|
|
||||||
return workload.GetKind(), workload.GetName(), err
|
|
||||||
}
|
|
||||||
if len(ownerReferences) == 0 {
|
|
||||||
return workload.GetKind(), workload.GetName(), nil // parent found
|
|
||||||
}
|
|
||||||
ownerReference := ownerReferences[0]
|
|
||||||
|
|
||||||
parentWorkload, err := k8sAPI.GetWorkload(workload.GetNamespace(), ownerReference.Kind, ownerReference.Name)
|
|
||||||
if err != nil {
|
|
||||||
if strings.Contains(err.Error(), "not found in resourceMap") { // if parent is RCD
|
|
||||||
return workload.GetKind(), workload.GetName(), nil // parent found
|
|
||||||
}
|
|
||||||
return workload.GetKind(), workload.GetName(), err
|
|
||||||
}
|
|
||||||
return k8sAPI.CalculateWorkloadParentRecursive(parentWorkload)
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
|
||||||
dynamicfake "k8s.io/client-go/dynamic/fake"
|
|
||||||
kubernetesfake "k8s.io/client-go/kubernetes/fake"
|
|
||||||
//
|
|
||||||
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
// Uncomment to load all auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth
|
|
||||||
//
|
|
||||||
// Or uncomment to load specific auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewKubernetesApi -
|
|
||||||
func NewKubernetesApiMock() *KubernetesApi {
|
|
||||||
|
|
||||||
return &KubernetesApi{
|
|
||||||
KubernetesClient: kubernetesfake.NewSimpleClientset(),
|
|
||||||
DynamicClient: dynamicfake.NewSimpleDynamicClient(&runtime.Scheme{}),
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// func TestListDynamic(t *testing.T) {
|
|
||||||
// k8s := NewKubernetesApi()
|
|
||||||
// resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
|
|
||||||
// clientResource, err := k8s.DynamicClient.Resource(resource).Namespace("default").List(k8s.Context, metav1.ListOptions{})
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("err: %v", err)
|
|
||||||
// } else {
|
|
||||||
// bla, _ := json.Marshal(clientResource)
|
|
||||||
// // t.Errorf("BearerToken: %v", *K8SConfig)
|
|
||||||
// // ioutil.WriteFile("bla.json", bla, 777)
|
|
||||||
// t.Errorf("clientResource: %s", string(bla))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Uncomment to load all auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth
|
|
||||||
//
|
|
||||||
// Or uncomment to load specific auth plugins
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
|
|
||||||
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
|
|
||||||
|
|
||||||
func ConvertUnstructuredSliceToMap(unstructuredSlice []unstructured.Unstructured) []map[string]interface{} {
|
|
||||||
converted := make([]map[string]interface{}, len(unstructuredSlice))
|
|
||||||
for i := range unstructuredSlice {
|
|
||||||
converted[i] = unstructuredSlice[i].Object
|
|
||||||
}
|
|
||||||
return converted
|
|
||||||
}
|
|
||||||
|
|
||||||
func FilterOutOwneredResources(result []unstructured.Unstructured) []unstructured.Unstructured {
|
|
||||||
response := []unstructured.Unstructured{}
|
|
||||||
recognizedOwners := []string{"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"}
|
|
||||||
for i := range result {
|
|
||||||
ownerReferences := result[i].GetOwnerReferences()
|
|
||||||
if len(ownerReferences) == 0 {
|
|
||||||
response = append(response, result[i])
|
|
||||||
} else if !IsStringInSlice(recognizedOwners, ownerReferences[0].Kind) {
|
|
||||||
response = append(response, result[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsStringInSlice(slice []string, val string) bool {
|
|
||||||
for _, item := range slice {
|
|
||||||
if item == val {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns all labels listed as a human readable string.
|
|
||||||
// Conveniently, exactly the format that ParseSelector takes.
|
|
||||||
func SelectorToString(ls labels.Set) string {
|
|
||||||
selector := make([]string, 0, len(ls))
|
|
||||||
for key, value := range ls {
|
|
||||||
if value != "" {
|
|
||||||
selector = append(selector, key+"="+value)
|
|
||||||
} else {
|
|
||||||
selector = append(selector, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sort for determinism.
|
|
||||||
sort.StringSlice(selector).Sort()
|
|
||||||
return strings.Join(selector, ",")
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestConvertUnstructuredSliceToMap(t *testing.T) {
|
|
||||||
converted := ConvertUnstructuredSliceToMap(V1KubeSystemNamespaceMock().Items)
|
|
||||||
if len(converted) == 0 { // != 7
|
|
||||||
t.Errorf("len(converted) == 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/cautils"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsAttached(labels map[string]string) *bool {
|
|
||||||
return IsLabel(labels, cautils.ArmoAttach)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsAgentCompatibleLabel(labels map[string]string) *bool {
|
|
||||||
return IsLabel(labels, cautils.ArmoCompatibleLabel)
|
|
||||||
}
|
|
||||||
func IsAgentCompatibleAnnotation(annotations map[string]string) *bool {
|
|
||||||
return IsLabel(annotations, cautils.ArmoCompatibleAnnotation)
|
|
||||||
}
|
|
||||||
func SetAgentCompatibleLabel(labels map[string]string, val bool) {
|
|
||||||
SetLabel(labels, cautils.ArmoCompatibleLabel, val)
|
|
||||||
}
|
|
||||||
func SetAgentCompatibleAnnotation(annotations map[string]string, val bool) {
|
|
||||||
SetLabel(annotations, cautils.ArmoCompatibleAnnotation, val)
|
|
||||||
}
|
|
||||||
func IsLabel(labels map[string]string, key string) *bool {
|
|
||||||
if labels == nil || len(labels) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var k bool
|
|
||||||
if l, ok := labels[key]; ok {
|
|
||||||
if l == "true" {
|
|
||||||
k = true
|
|
||||||
} else if l == "false" {
|
|
||||||
k = false
|
|
||||||
}
|
|
||||||
return &k
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func SetLabel(labels map[string]string, key string, val bool) {
|
|
||||||
if labels == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
v := ""
|
|
||||||
if val {
|
|
||||||
v = "true"
|
|
||||||
} else {
|
|
||||||
v = "false"
|
|
||||||
}
|
|
||||||
labels[key] = v
|
|
||||||
}
|
|
||||||
func (k8sAPI *KubernetesApi) ListAttachedPods(namespace string) ([]corev1.Pod, error) {
|
|
||||||
return k8sAPI.ListPods(namespace, map[string]string{cautils.ArmoAttach: cautils.BoolToString(true)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k8sAPI *KubernetesApi) ListPods(namespace string, podLabels map[string]string) ([]corev1.Pod, error) {
|
|
||||||
listOptions := metav1.ListOptions{}
|
|
||||||
if podLabels != nil && len(podLabels) > 0 {
|
|
||||||
set := labels.Set(podLabels)
|
|
||||||
listOptions.LabelSelector = set.AsSelector().String()
|
|
||||||
}
|
|
||||||
pods, err := k8sAPI.KubernetesClient.CoreV1().Pods(namespace).List(context.Background(), listOptions)
|
|
||||||
if err != nil {
|
|
||||||
return []corev1.Pod{}, err
|
|
||||||
}
|
|
||||||
return pods.Items, nil
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,142 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
)
|
|
||||||
|
|
||||||
const ValueNotFound = -1
|
|
||||||
|
|
||||||
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#-strong-api-groups-strong-
|
|
||||||
var ResourceGroupMapping = map[string]string{
|
|
||||||
"services": "/v1",
|
|
||||||
"pods": "/v1",
|
|
||||||
"replicationcontrollers": "/v1",
|
|
||||||
"podtemplates": "/v1",
|
|
||||||
"namespaces": "/v1",
|
|
||||||
"nodes": "/v1",
|
|
||||||
"configmaps": "/v1",
|
|
||||||
"secrets": "/v1",
|
|
||||||
"serviceaccounts": "/v1",
|
|
||||||
"persistentvolumeclaims": "/v1",
|
|
||||||
"limitranges": "/v1",
|
|
||||||
"resourcequotas": "/v1",
|
|
||||||
"daemonsets": "apps/v1",
|
|
||||||
"deployments": "apps/v1",
|
|
||||||
"replicasets": "apps/v1",
|
|
||||||
"statefulsets": "apps/v1",
|
|
||||||
"controllerrevisions": "apps/v1",
|
|
||||||
"jobs": "batch/v1",
|
|
||||||
"cronjobs": "batch/v1beta1",
|
|
||||||
"horizontalpodautoscalers": "autoscaling/v1",
|
|
||||||
"ingresses": "extensions/v1beta1",
|
|
||||||
"networkpolicies": "networking.k8s.io/v1",
|
|
||||||
"clusterroles": "rbac.authorization.k8s.io/v1",
|
|
||||||
"clusterrolebindings": "rbac.authorization.k8s.io/v1",
|
|
||||||
"roles": "rbac.authorization.k8s.io/v1",
|
|
||||||
"rolebindings": "rbac.authorization.k8s.io/v1",
|
|
||||||
"mutatingwebhookconfigurations": "admissionregistration.k8s.io/v1",
|
|
||||||
"validatingwebhookconfigurations": "admissionregistration.k8s.io/v1",
|
|
||||||
}
|
|
||||||
|
|
||||||
var GroupsClusterScope = []string{}
|
|
||||||
var ResourceClusterScope = []string{"nodes", "namespaces", "clusterroles", "clusterrolebindings"}
|
|
||||||
|
|
||||||
func GetGroupVersionResource(resource string) (schema.GroupVersionResource, error) {
|
|
||||||
resource = updateResourceKind(resource)
|
|
||||||
if r, ok := ResourceGroupMapping[resource]; ok {
|
|
||||||
gv := strings.Split(r, "/")
|
|
||||||
return schema.GroupVersionResource{Group: gv[0], Version: gv[1], Resource: resource}, nil
|
|
||||||
}
|
|
||||||
return schema.GroupVersionResource{}, fmt.Errorf("resource '%s' not found in resourceMap", resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNamespaceScope(apiGroup, resource string) bool {
|
|
||||||
return StringInSlice(GroupsClusterScope, apiGroup) == ValueNotFound &&
|
|
||||||
StringInSlice(ResourceClusterScope, resource) == ValueNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringInSlice(strSlice []string, str string) int {
|
|
||||||
for i := range strSlice {
|
|
||||||
if strSlice[i] == str {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ValueNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
func JoinResourceTriplets(group, version, resource string) string {
|
|
||||||
return fmt.Sprintf("%s/%s/%s", group, version, resource)
|
|
||||||
}
|
|
||||||
func GetResourceTriplets(group, version, resource string) []string {
|
|
||||||
resourceTriplets := []string{}
|
|
||||||
if resource == "" {
|
|
||||||
// load full map
|
|
||||||
for k, v := range ResourceGroupMapping {
|
|
||||||
g := strings.Split(v, "/")
|
|
||||||
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(g[0], g[1], k))
|
|
||||||
}
|
|
||||||
} else if version == "" {
|
|
||||||
// load by resource
|
|
||||||
if v, ok := ResourceGroupMapping[resource]; ok {
|
|
||||||
g := strings.Split(v, "/")
|
|
||||||
if group == "" {
|
|
||||||
group = g[0]
|
|
||||||
}
|
|
||||||
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(group, g[1], resource))
|
|
||||||
} else {
|
|
||||||
glog.Errorf("Resource '%s' unknown", resource)
|
|
||||||
}
|
|
||||||
} else if group == "" {
|
|
||||||
// load by resource and version
|
|
||||||
if v, ok := ResourceGroupMapping[resource]; ok {
|
|
||||||
g := strings.Split(v, "/")
|
|
||||||
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(g[0], version, resource))
|
|
||||||
} else {
|
|
||||||
glog.Errorf("Resource '%s' unknown", resource)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(group, version, resource))
|
|
||||||
}
|
|
||||||
return resourceTriplets
|
|
||||||
}
|
|
||||||
func ResourceGroupToString(group, version, resource string) []string {
|
|
||||||
if group == "*" {
|
|
||||||
group = ""
|
|
||||||
}
|
|
||||||
if version == "*" {
|
|
||||||
version = ""
|
|
||||||
}
|
|
||||||
if resource == "*" {
|
|
||||||
resource = ""
|
|
||||||
}
|
|
||||||
resource = updateResourceKind(resource)
|
|
||||||
return GetResourceTriplets(group, version, resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
func StringToResourceGroup(str string) (string, string, string) {
|
|
||||||
splitted := strings.Split(str, "/")
|
|
||||||
for i := range splitted {
|
|
||||||
if splitted[i] == "*" {
|
|
||||||
splitted[i] = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return splitted[0], splitted[1], splitted[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateResourceKind(resource string) string {
|
|
||||||
resource = strings.ToLower(resource)
|
|
||||||
|
|
||||||
if resource != "" && !strings.HasSuffix(resource, "s") {
|
|
||||||
if strings.HasSuffix(resource, "y") {
|
|
||||||
return fmt.Sprintf("%sies", strings.TrimSuffix(resource, "y")) // e.g. NetworkPolicy -> networkpolicies
|
|
||||||
} else {
|
|
||||||
return fmt.Sprintf("%ss", resource) // add 's' at the end of a resource
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resource
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestResourceGroupToString(t *testing.T) {
|
|
||||||
allResources := ResourceGroupToString("*", "*", "*")
|
|
||||||
if len(allResources) != len(ResourceGroupMapping) {
|
|
||||||
t.Errorf("Expected len: %d, received: %d", len(ResourceGroupMapping), len(allResources))
|
|
||||||
}
|
|
||||||
pod := ResourceGroupToString("*", "*", "Pod")
|
|
||||||
if len(pod) == 0 || pod[0] != "/v1/pods" {
|
|
||||||
t.Errorf("pod: %v", pod)
|
|
||||||
}
|
|
||||||
deployments := ResourceGroupToString("*", "*", "Deployment")
|
|
||||||
if len(deployments) == 0 || deployments[0] != "apps/v1/deployments" {
|
|
||||||
t.Errorf("deployments: %v", deployments)
|
|
||||||
}
|
|
||||||
cronjobs := ResourceGroupToString("*", "*", "cronjobs")
|
|
||||||
if len(cronjobs) == 0 || cronjobs[0] != "batch/v1beta1/cronjobs" {
|
|
||||||
t.Errorf("cronjobs: %v", cronjobs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/apis"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IWorkload interface {
|
|
||||||
IBasicWorkload
|
|
||||||
|
|
||||||
// Convert
|
|
||||||
ToUnstructured() (*unstructured.Unstructured, error)
|
|
||||||
ToString() string
|
|
||||||
Json() string // DEPRECATED
|
|
||||||
|
|
||||||
// GET
|
|
||||||
GetWlid() string
|
|
||||||
GetJobID() *apis.JobTracking
|
|
||||||
GetVersion() string
|
|
||||||
GetGroup() string
|
|
||||||
|
|
||||||
// SET
|
|
||||||
SetWlid(string)
|
|
||||||
SetInject()
|
|
||||||
SetIgnore()
|
|
||||||
SetUpdateTime()
|
|
||||||
SetJobID(apis.JobTracking)
|
|
||||||
SetCompatible()
|
|
||||||
SetIncompatible()
|
|
||||||
SetReplaceheaders()
|
|
||||||
|
|
||||||
// EXIST
|
|
||||||
IsIgnore() bool
|
|
||||||
IsInject() bool
|
|
||||||
IsAttached() bool
|
|
||||||
IsCompatible() bool
|
|
||||||
IsIncompatible() bool
|
|
||||||
|
|
||||||
// REMOVE
|
|
||||||
RemoveWlid()
|
|
||||||
RemoveSecretData()
|
|
||||||
RemoveInject()
|
|
||||||
RemoveIgnore()
|
|
||||||
RemoveUpdateTime()
|
|
||||||
RemoveJobID()
|
|
||||||
RemoveCompatible()
|
|
||||||
RemoveArmoMetadata()
|
|
||||||
RemoveArmoLabels()
|
|
||||||
RemoveArmoAnnotations()
|
|
||||||
}
|
|
||||||
type IBasicWorkload interface {
|
|
||||||
|
|
||||||
// Set
|
|
||||||
SetKind(string)
|
|
||||||
SetWorkload(map[string]interface{})
|
|
||||||
SetLabel(key, value string)
|
|
||||||
SetAnnotation(key, value string)
|
|
||||||
SetNamespace(string)
|
|
||||||
SetName(string)
|
|
||||||
|
|
||||||
// Get
|
|
||||||
GetNamespace() string
|
|
||||||
GetName() string
|
|
||||||
GetGenerateName() string
|
|
||||||
GetApiVersion() string
|
|
||||||
GetKind() string
|
|
||||||
GetInnerAnnotation(string) (string, bool)
|
|
||||||
GetPodAnnotation(string) (string, bool)
|
|
||||||
GetAnnotation(string) (string, bool)
|
|
||||||
GetLabel(string) (string, bool)
|
|
||||||
GetAnnotations() map[string]string
|
|
||||||
GetInnerAnnotations() map[string]string
|
|
||||||
GetPodAnnotations() map[string]string
|
|
||||||
GetLabels() map[string]string
|
|
||||||
GetInnerLabels() map[string]string
|
|
||||||
GetPodLabels() map[string]string
|
|
||||||
GetVolumes() ([]corev1.Volume, error)
|
|
||||||
GetReplicas() int
|
|
||||||
GetContainers() ([]corev1.Container, error)
|
|
||||||
GetInitContainers() ([]corev1.Container, error)
|
|
||||||
GetOwnerReferences() ([]metav1.OwnerReference, error)
|
|
||||||
GetImagePullSecret() ([]corev1.LocalObjectReference, error)
|
|
||||||
GetServiceAccountName() string
|
|
||||||
GetSelector() (*metav1.LabelSelector, error)
|
|
||||||
GetResourceVersion() string
|
|
||||||
GetUID() string
|
|
||||||
GetPodSpec() (*corev1.PodSpec, error)
|
|
||||||
|
|
||||||
GetWorkload() map[string]interface{}
|
|
||||||
|
|
||||||
// REMOVE
|
|
||||||
RemoveLabel(string)
|
|
||||||
RemoveAnnotation(string)
|
|
||||||
RemovePodStatus()
|
|
||||||
RemoveResourceVersion()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Workload struct {
|
|
||||||
workload map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWorkload(bWorkload []byte) (*Workload, error) {
|
|
||||||
workload := make(map[string]interface{})
|
|
||||||
if bWorkload != nil {
|
|
||||||
if err := json.Unmarshal(bWorkload, &workload); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &Workload{
|
|
||||||
workload: workload,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWorkloadObj(workload map[string]interface{}) *Workload {
|
|
||||||
return &Workload{
|
|
||||||
workload: workload,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) Json() string {
|
|
||||||
return w.ToString()
|
|
||||||
}
|
|
||||||
func (w *Workload) ToString() string {
|
|
||||||
if w.GetWorkload() == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
bWorkload, err := json.Marshal(w.GetWorkload())
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return string(bWorkload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (workload *Workload) DeepCopy(w map[string]interface{}) {
|
|
||||||
workload.workload = make(map[string]interface{})
|
|
||||||
byt, _ := json.Marshal(w)
|
|
||||||
json.Unmarshal(byt, &workload.workload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) ToUnstructured() (*unstructured.Unstructured, error) {
|
|
||||||
obj := &unstructured.Unstructured{}
|
|
||||||
if w.workload == nil {
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
bWorkload, err := json.Marshal(w.workload)
|
|
||||||
if err != nil {
|
|
||||||
return obj, err
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(bWorkload, obj); err != nil {
|
|
||||||
return obj, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
@@ -1,642 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/apis"
|
|
||||||
"github.com/armosec/kubescape/cautils/cautils"
|
|
||||||
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ======================================= DELETE ========================================
|
|
||||||
|
|
||||||
func (w *Workload) RemoveInject() {
|
|
||||||
w.RemovePodLabel(cautils.CAInject) // DEPRECATED
|
|
||||||
w.RemovePodLabel(cautils.CAAttachLabel) // DEPRECATED
|
|
||||||
w.RemovePodLabel(cautils.ArmoAttach)
|
|
||||||
|
|
||||||
w.RemoveLabel(cautils.CAInject) // DEPRECATED
|
|
||||||
w.RemoveLabel(cautils.CAAttachLabel) // DEPRECATED
|
|
||||||
w.RemoveLabel(cautils.ArmoAttach)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveIgnore() {
|
|
||||||
w.RemovePodLabel(cautils.CAIgnore) // DEPRECATED
|
|
||||||
w.RemovePodLabel(cautils.ArmoAttach)
|
|
||||||
|
|
||||||
w.RemoveLabel(cautils.CAIgnore) // DEPRECATED
|
|
||||||
w.RemoveLabel(cautils.ArmoAttach)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveWlid() {
|
|
||||||
w.RemovePodAnnotation(cautils.CAWlid) // DEPRECATED
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoWlid)
|
|
||||||
|
|
||||||
w.RemoveAnnotation(cautils.CAWlid) // DEPRECATED
|
|
||||||
w.RemoveAnnotation(cautils.ArmoWlid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveCompatible() {
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoCompatibleAnnotation)
|
|
||||||
}
|
|
||||||
func (w *Workload) RemoveJobID() {
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoJobIDPath)
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoJobParentPath)
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoJobActionPath)
|
|
||||||
|
|
||||||
w.RemoveAnnotation(cautils.ArmoJobIDPath)
|
|
||||||
w.RemoveAnnotation(cautils.ArmoJobParentPath)
|
|
||||||
w.RemoveAnnotation(cautils.ArmoJobActionPath)
|
|
||||||
}
|
|
||||||
func (w *Workload) RemoveArmoMetadata() {
|
|
||||||
w.RemoveArmoLabels()
|
|
||||||
w.RemoveArmoAnnotations()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveArmoAnnotations() {
|
|
||||||
l := w.GetAnnotations()
|
|
||||||
if l != nil {
|
|
||||||
for k := range l {
|
|
||||||
if strings.HasPrefix(k, cautils.ArmoPrefix) {
|
|
||||||
w.RemoveAnnotation(k)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
|
|
||||||
w.RemoveAnnotation(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lp := w.GetPodAnnotations()
|
|
||||||
if lp != nil {
|
|
||||||
for k := range lp {
|
|
||||||
if strings.HasPrefix(k, cautils.ArmoPrefix) {
|
|
||||||
w.RemovePodAnnotation(k)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
|
|
||||||
w.RemovePodAnnotation(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (w *Workload) RemoveArmoLabels() {
|
|
||||||
l := w.GetLabels()
|
|
||||||
if l != nil {
|
|
||||||
for k := range l {
|
|
||||||
if strings.HasPrefix(k, cautils.ArmoPrefix) {
|
|
||||||
w.RemoveLabel(k)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
|
|
||||||
w.RemoveLabel(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lp := w.GetPodLabels()
|
|
||||||
if lp != nil {
|
|
||||||
for k := range lp {
|
|
||||||
if strings.HasPrefix(k, cautils.ArmoPrefix) {
|
|
||||||
w.RemovePodLabel(k)
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
|
|
||||||
w.RemovePodLabel(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func (w *Workload) RemoveUpdateTime() {
|
|
||||||
|
|
||||||
// remove from pod
|
|
||||||
w.RemovePodAnnotation(cautils.CAUpdate) // DEPRECATED
|
|
||||||
w.RemovePodAnnotation(cautils.ArmoUpdate)
|
|
||||||
|
|
||||||
// remove from workload
|
|
||||||
w.RemoveAnnotation(cautils.CAUpdate) // DEPRECATED
|
|
||||||
w.RemoveAnnotation(cautils.ArmoUpdate)
|
|
||||||
}
|
|
||||||
func (w *Workload) RemoveSecretData() {
|
|
||||||
w.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
|
|
||||||
delete(w.workload, "data")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemovePodStatus() {
|
|
||||||
delete(w.workload, "status")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveResourceVersion() {
|
|
||||||
if _, ok := w.workload["metadata"]; !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
meta, _ := w.workload["metadata"].(map[string]interface{})
|
|
||||||
delete(meta, "resourceVersion")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveLabel(key string) {
|
|
||||||
w.RemoveMetadata([]string{"metadata"}, "labels", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveAnnotation(key string) {
|
|
||||||
w.RemoveMetadata([]string{"metadata"}, "annotations", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemovePodAnnotation(key string) {
|
|
||||||
w.RemoveMetadata(PodMetadata(w.GetKind()), "annotations", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemovePodLabel(key string) {
|
|
||||||
w.RemoveMetadata(PodMetadata(w.GetKind()), "labels", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) RemoveMetadata(scope []string, metadata, key string) {
|
|
||||||
|
|
||||||
workload := w.workload
|
|
||||||
for i := range scope {
|
|
||||||
if _, ok := workload[scope[i]]; !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
workload, _ = workload[scope[i]].(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := workload[metadata]; !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
labels, _ := workload[metadata].(map[string]interface{})
|
|
||||||
delete(labels, key)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================= SET =========================================
|
|
||||||
|
|
||||||
func (w *Workload) SetWorkload(workload map[string]interface{}) {
|
|
||||||
w.workload = workload
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetKind(kind string) {
|
|
||||||
w.workload["kind"] = kind
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetInject() {
|
|
||||||
w.SetPodLabel(cautils.ArmoAttach, cautils.BoolToString(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetJobID(jobTracking apis.JobTracking) {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoJobIDPath, jobTracking.JobID)
|
|
||||||
w.SetPodAnnotation(cautils.ArmoJobParentPath, jobTracking.ParentID)
|
|
||||||
w.SetPodAnnotation(cautils.ArmoJobActionPath, fmt.Sprintf("%d", jobTracking.LastActionNumber))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetIgnore() {
|
|
||||||
w.SetPodLabel(cautils.ArmoAttach, cautils.BoolToString(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetCompatible() {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoCompatibleAnnotation, cautils.BoolToString(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetIncompatible() {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoCompatibleAnnotation, cautils.BoolToString(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetReplaceheaders() {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoReplaceheaders, cautils.BoolToString(true))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetWlid(wlid string) {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoWlid, wlid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetUpdateTime() {
|
|
||||||
w.SetPodAnnotation(cautils.ArmoUpdate, string(time.Now().UTC().Format("02-01-2006 15:04:05")))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetNamespace(namespace string) {
|
|
||||||
w.SetMetadata([]string{"metadata"}, "namespace", namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetName(name string) {
|
|
||||||
w.SetMetadata([]string{"metadata"}, "name", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetLabel(key, value string) {
|
|
||||||
w.SetMetadata([]string{"metadata", "labels"}, key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetPodLabel(key, value string) {
|
|
||||||
w.SetMetadata(append(PodMetadata(w.GetKind()), "labels"), key, value)
|
|
||||||
}
|
|
||||||
func (w *Workload) SetAnnotation(key, value string) {
|
|
||||||
w.SetMetadata([]string{"metadata", "annotations"}, key, value)
|
|
||||||
}
|
|
||||||
func (w *Workload) SetPodAnnotation(key, value string) {
|
|
||||||
w.SetMetadata(append(PodMetadata(w.GetKind()), "annotations"), key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) SetMetadata(scope []string, key string, val interface{}) {
|
|
||||||
workload := w.workload
|
|
||||||
for i := range scope {
|
|
||||||
if _, ok := workload[scope[i]]; !ok {
|
|
||||||
workload[scope[i]] = make(map[string]interface{})
|
|
||||||
}
|
|
||||||
workload, _ = workload[scope[i]].(map[string]interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
workload[key] = val
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================= GET =========================================
|
|
||||||
func (w *Workload) GetWorkload() map[string]interface{} {
|
|
||||||
return w.workload
|
|
||||||
}
|
|
||||||
func (w *Workload) GetNamespace() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "namespace"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetName() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "name"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetApiVersion() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "apiVersion"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetVersion() string {
|
|
||||||
apiVersion := w.GetApiVersion()
|
|
||||||
splitted := strings.Split(apiVersion, "/")
|
|
||||||
if len(splitted) == 1 {
|
|
||||||
return splitted[0]
|
|
||||||
} else if len(splitted) == 2 {
|
|
||||||
return splitted[1]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetGroup() string {
|
|
||||||
apiVersion := w.GetApiVersion()
|
|
||||||
splitted := strings.Split(apiVersion, "/")
|
|
||||||
if len(splitted) == 2 {
|
|
||||||
return splitted[0]
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetGenerateName() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "generateName"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetReplicas() int {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "spec", "replicas"); ok {
|
|
||||||
replicas, isok := v.(float64)
|
|
||||||
if isok {
|
|
||||||
return int(replicas)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetKind() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "kind"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (w *Workload) GetSelector() (*metav1.LabelSelector, error) {
|
|
||||||
selector := &metav1.LabelSelector{}
|
|
||||||
if v, ok := InspectWorkload(w.workload, "spec", "selector", "matchLabels"); ok && v != nil {
|
|
||||||
b, err := json.Marshal(v)
|
|
||||||
if err != nil {
|
|
||||||
return selector, err
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(b, selector); err != nil {
|
|
||||||
return selector, err
|
|
||||||
}
|
|
||||||
return selector, nil
|
|
||||||
}
|
|
||||||
return selector, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetAnnotation(annotation string) (string, bool) {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "annotations", annotation); ok {
|
|
||||||
return v.(string), ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
func (w *Workload) GetLabel(label string) (string, bool) {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "labels", label); ok {
|
|
||||||
return v.(string), ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetPodLabel(label string) (string, bool) {
|
|
||||||
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "labels", label)...); ok && v != nil {
|
|
||||||
return v.(string), ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetLabels() map[string]string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "labels"); ok && v != nil {
|
|
||||||
labels := make(map[string]string)
|
|
||||||
for k, i := range v.(map[string]interface{}) {
|
|
||||||
labels[k] = i.(string)
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInnerLabels - DEPRECATED
|
|
||||||
func (w *Workload) GetInnerLabels() map[string]string {
|
|
||||||
return w.GetPodLabels()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetPodLabels() map[string]string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "labels")...); ok && v != nil {
|
|
||||||
labels := make(map[string]string)
|
|
||||||
for k, i := range v.(map[string]interface{}) {
|
|
||||||
labels[k] = i.(string)
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInnerAnnotations - DEPRECATED
|
|
||||||
func (w *Workload) GetInnerAnnotations() map[string]string {
|
|
||||||
return w.GetPodAnnotations()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPodAnnotations
|
|
||||||
func (w *Workload) GetPodAnnotations() map[string]string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "annotations")...); ok && v != nil {
|
|
||||||
annotations := make(map[string]string)
|
|
||||||
for k, i := range v.(map[string]interface{}) {
|
|
||||||
annotations[k] = fmt.Sprintf("%v", i)
|
|
||||||
}
|
|
||||||
return annotations
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInnerAnnotation DEPRECATED
|
|
||||||
func (w *Workload) GetInnerAnnotation(annotation string) (string, bool) {
|
|
||||||
return w.GetPodAnnotation(annotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetPodAnnotation(annotation string) (string, bool) {
|
|
||||||
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "annotations", annotation)...); ok && v != nil {
|
|
||||||
return v.(string), ok
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetAnnotations() map[string]string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "annotations"); ok && v != nil {
|
|
||||||
annotations := make(map[string]string)
|
|
||||||
for k, i := range v.(map[string]interface{}) {
|
|
||||||
annotations[k] = fmt.Sprintf("%v", i)
|
|
||||||
}
|
|
||||||
return annotations
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetVolumes -
|
|
||||||
func (w *Workload) GetVolumes() ([]corev1.Volume, error) {
|
|
||||||
volumes := []corev1.Volume{}
|
|
||||||
|
|
||||||
interVolumes, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "volumes")...)
|
|
||||||
if interVolumes == nil {
|
|
||||||
return volumes, nil
|
|
||||||
}
|
|
||||||
volumesBytes, err := json.Marshal(interVolumes)
|
|
||||||
if err != nil {
|
|
||||||
return volumes, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(volumesBytes, &volumes)
|
|
||||||
|
|
||||||
return volumes, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetServiceAccountName() string {
|
|
||||||
|
|
||||||
if v, ok := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "serviceAccountName")...); ok && v != nil {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetPodSpec() (*corev1.PodSpec, error) {
|
|
||||||
podSpec := &corev1.PodSpec{}
|
|
||||||
podSepcRaw, _ := InspectWorkload(w.workload, PodSpec(w.GetKind())...)
|
|
||||||
if podSepcRaw == nil {
|
|
||||||
return podSpec, fmt.Errorf("no PodSpec for workload: %v", w)
|
|
||||||
}
|
|
||||||
b, err := json.Marshal(podSepcRaw)
|
|
||||||
if err != nil {
|
|
||||||
return podSpec, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(b, podSpec)
|
|
||||||
|
|
||||||
return podSpec, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetImagePullSecret() ([]corev1.LocalObjectReference, error) {
|
|
||||||
imgPullSecrets := []corev1.LocalObjectReference{}
|
|
||||||
|
|
||||||
iImgPullSecrets, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "imagePullSecrets")...)
|
|
||||||
b, err := json.Marshal(iImgPullSecrets)
|
|
||||||
if err != nil {
|
|
||||||
return imgPullSecrets, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(b, &imgPullSecrets)
|
|
||||||
|
|
||||||
return imgPullSecrets, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContainers -
|
|
||||||
func (w *Workload) GetContainers() ([]corev1.Container, error) {
|
|
||||||
containers := []corev1.Container{}
|
|
||||||
|
|
||||||
interContainers, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "containers")...)
|
|
||||||
if interContainers == nil {
|
|
||||||
return containers, nil
|
|
||||||
}
|
|
||||||
containersBytes, err := json.Marshal(interContainers)
|
|
||||||
if err != nil {
|
|
||||||
return containers, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(containersBytes, &containers)
|
|
||||||
|
|
||||||
return containers, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInitContainers -
|
|
||||||
func (w *Workload) GetInitContainers() ([]corev1.Container, error) {
|
|
||||||
containers := []corev1.Container{}
|
|
||||||
|
|
||||||
interContainers, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "initContainers")...)
|
|
||||||
if interContainers == nil {
|
|
||||||
return containers, nil
|
|
||||||
}
|
|
||||||
containersBytes, err := json.Marshal(interContainers)
|
|
||||||
if err != nil {
|
|
||||||
return containers, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(containersBytes, &containers)
|
|
||||||
|
|
||||||
return containers, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetOwnerReferences -
|
|
||||||
func (w *Workload) GetOwnerReferences() ([]metav1.OwnerReference, error) {
|
|
||||||
ownerReferences := []metav1.OwnerReference{}
|
|
||||||
interOwnerReferences, ok := InspectWorkload(w.workload, "metadata", "ownerReferences")
|
|
||||||
if !ok {
|
|
||||||
return ownerReferences, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ownerReferencesBytes, err := json.Marshal(interOwnerReferences)
|
|
||||||
if err != nil {
|
|
||||||
return ownerReferences, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(ownerReferencesBytes, &ownerReferences)
|
|
||||||
if err != nil {
|
|
||||||
return ownerReferences, err
|
|
||||||
|
|
||||||
}
|
|
||||||
return ownerReferences, nil
|
|
||||||
}
|
|
||||||
func (w *Workload) GetResourceVersion() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "resourceVersion"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (w *Workload) GetUID() string {
|
|
||||||
if v, ok := InspectWorkload(w.workload, "metadata", "uid"); ok {
|
|
||||||
return v.(string)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
func (w *Workload) GetWlid() string {
|
|
||||||
if wlid, ok := w.GetAnnotation(cautils.ArmoWlid); ok {
|
|
||||||
return wlid
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) GetJobID() *apis.JobTracking {
|
|
||||||
jobTracking := apis.JobTracking{}
|
|
||||||
if job, ok := w.GetPodAnnotation(cautils.ArmoJobIDPath); ok {
|
|
||||||
jobTracking.JobID = job
|
|
||||||
}
|
|
||||||
if parent, ok := w.GetPodAnnotation(cautils.ArmoJobParentPath); ok {
|
|
||||||
jobTracking.ParentID = parent
|
|
||||||
}
|
|
||||||
if action, ok := w.GetPodAnnotation(cautils.ArmoJobActionPath); ok {
|
|
||||||
if i, err := strconv.Atoi(action); err == nil {
|
|
||||||
jobTracking.LastActionNumber = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if jobTracking.LastActionNumber == 0 { // start the counter at 1
|
|
||||||
jobTracking.LastActionNumber = 1
|
|
||||||
}
|
|
||||||
return &jobTracking
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (w *Workload) GetJobID() string {
|
|
||||||
// if status, ok := w.GetAnnotation(cautils.ArmoJobID); ok {
|
|
||||||
// return status
|
|
||||||
// }
|
|
||||||
// return ""
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ========================================= IS =========================================
|
|
||||||
|
|
||||||
func (w *Workload) IsInject() bool {
|
|
||||||
return w.IsAttached()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) IsIgnore() bool {
|
|
||||||
if attach := cautils.IsAttached(w.GetPodLabels()); attach != nil {
|
|
||||||
return !(*attach)
|
|
||||||
}
|
|
||||||
if attach := cautils.IsAttached(w.GetLabels()); attach != nil {
|
|
||||||
return !(*attach)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) IsCompatible() bool {
|
|
||||||
if c, ok := w.GetPodAnnotation(cautils.ArmoCompatibleAnnotation); ok {
|
|
||||||
return cautils.StringToBool(c)
|
|
||||||
|
|
||||||
}
|
|
||||||
if c, ok := w.GetAnnotation(cautils.ArmoCompatibleAnnotation); ok {
|
|
||||||
return cautils.StringToBool(c)
|
|
||||||
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) IsIncompatible() bool {
|
|
||||||
if c, ok := w.GetPodAnnotation(cautils.ArmoCompatibleAnnotation); ok {
|
|
||||||
return !cautils.StringToBool(c)
|
|
||||||
}
|
|
||||||
if c, ok := w.GetAnnotation(cautils.ArmoCompatibleAnnotation); ok {
|
|
||||||
return !cautils.StringToBool(c)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
func (w *Workload) IsAttached() bool {
|
|
||||||
if attach := cautils.IsAttached(w.GetPodLabels()); attach != nil {
|
|
||||||
return *attach
|
|
||||||
}
|
|
||||||
if attach := cautils.IsAttached(w.GetLabels()); attach != nil {
|
|
||||||
return *attach
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *Workload) IsReplaceheaders() bool {
|
|
||||||
if c, ok := w.GetPodAnnotation(cautils.ArmoReplaceheaders); ok {
|
|
||||||
return cautils.StringToBool(c)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ======================================= UTILS =========================================
|
|
||||||
|
|
||||||
// InspectWorkload -
|
|
||||||
func InspectWorkload(workload interface{}, scopes ...string) (val interface{}, k bool) {
|
|
||||||
|
|
||||||
val, k = nil, false
|
|
||||||
if len(scopes) == 0 {
|
|
||||||
if workload != nil {
|
|
||||||
return workload, true
|
|
||||||
}
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
if data, ok := workload.(map[string]interface{}); ok {
|
|
||||||
val, k = InspectWorkload(data[scopes[0]], scopes[1:]...)
|
|
||||||
}
|
|
||||||
return val, k
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ========================================= IS =========================================
|
|
||||||
|
|
||||||
func TestLabels(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"labels":{"app":"demoservice-server","cyberarmor.inject":"true"},"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if workload.GetKind() != "Deployment" {
|
|
||||||
t.Errorf("wrong kind")
|
|
||||||
}
|
|
||||||
if workload.GetNamespace() != "default" {
|
|
||||||
t.Errorf("wrong namespace")
|
|
||||||
}
|
|
||||||
if workload.GetName() != "demoservice-server" {
|
|
||||||
t.Errorf("wrong name")
|
|
||||||
}
|
|
||||||
if !workload.IsInject() {
|
|
||||||
t.Errorf("expect to find inject label")
|
|
||||||
}
|
|
||||||
if workload.IsIgnore() {
|
|
||||||
t.Errorf("expect to find ignore label")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetNamespace(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
workload.SetNamespace("default")
|
|
||||||
if workload.GetNamespace() != "default" {
|
|
||||||
t.Errorf("wrong namespace")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestSetLabels(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
workload.SetLabel("bla", "daa")
|
|
||||||
v, ok := workload.GetLabel("bla")
|
|
||||||
if !ok || v != "daa" {
|
|
||||||
t.Errorf("expect to find label")
|
|
||||||
}
|
|
||||||
workload.RemoveLabel("bla")
|
|
||||||
v2, ok2 := workload.GetLabel("bla")
|
|
||||||
if ok2 || v2 == "daa" {
|
|
||||||
t.Errorf("label not deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetAnnotations(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
workload.SetAnnotation("bla", "daa")
|
|
||||||
v, ok := workload.GetAnnotation("bla")
|
|
||||||
if !ok || v != "daa" {
|
|
||||||
t.Errorf("expect to find annotation")
|
|
||||||
}
|
|
||||||
workload.RemoveAnnotation("bla")
|
|
||||||
v2, ok2 := workload.GetAnnotation("bla")
|
|
||||||
if ok2 || v2 == "daa" {
|
|
||||||
t.Errorf("annotation not deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestSetPodLabels(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
workload.SetPodLabel("bla", "daa")
|
|
||||||
v, ok := workload.GetPodLabel("bla")
|
|
||||||
if !ok || v != "daa" {
|
|
||||||
t.Errorf("expect to find label")
|
|
||||||
}
|
|
||||||
workload.RemovePodLabel("bla")
|
|
||||||
v2, ok2 := workload.GetPodLabel("bla")
|
|
||||||
if ok2 || v2 == "daa" {
|
|
||||||
t.Errorf("label not deleted")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestRemoveArmo(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server", "armo.attach": "true"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if !workload.IsAttached() {
|
|
||||||
t.Errorf("expect to be attached")
|
|
||||||
}
|
|
||||||
workload.RemoveArmoMetadata()
|
|
||||||
if workload.IsAttached() {
|
|
||||||
t.Errorf("expect to be clear")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetWlid(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
workload.SetWlid("wlid://bla")
|
|
||||||
// t.Errorf(workload.Json())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetResourceVersion(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if workload.GetResourceVersion() != "1016043" {
|
|
||||||
t.Errorf("wrong resourceVersion")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestGetUID(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if workload.GetUID() != "e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e" {
|
|
||||||
t.Errorf("wrong UID")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIsAttached(t *testing.T) {
|
|
||||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"3"},"creationTimestamp":"2021-06-21T04:52:05Z","generation":3,"name":"emailservice","namespace":"default"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"emailservice"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"annotations":{"armo.last-update":"21-06-2021 06:40:42","armo.wlid":"wlid://cluster-david-demo/namespace-default/deployment-emailservice"},"creationTimestamp":null,"labels":{"app":"emailservice","armo.attach":"true"}},"spec":{"containers":[{"env":[{"name":"PORT","value":"8080"},{"name":"DISABLE_PROFILER","value":"1"}],"image":"gcr.io/google-samples/microservices-demo/emailservice:v0.2.3","imagePullPolicy":"IfNotPresent","livenessProbe":{"exec":{"command":["/bin/grpc_health_probe","-addr=:8080"]},"failureThreshold":3,"periodSeconds":5,"successThreshold":1,"timeoutSeconds":1},"name":"server","ports":[{"containerPort":8080,"protocol":"TCP"}],"readinessProbe":{"exec":{"command":["/bin/grpc_health_probe","-addr=:8080"]},"failureThreshold":3,"periodSeconds":5,"successThreshold":1,"timeoutSeconds":1},"resources":{"limits":{"cpu":"200m","memory":"128Mi"},"requests":{"cpu":"100m","memory":"64Mi"}},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":5}}}}`
|
|
||||||
workload, err := NewWorkload([]byte(w))
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if !workload.IsAttached() {
|
|
||||||
t.Errorf("expected attached")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package k8sinterface
|
|
||||||
|
|
||||||
func PodSpec(kind string) []string {
|
|
||||||
switch kind {
|
|
||||||
case "Pod", "Namespace":
|
|
||||||
return []string{"spec"}
|
|
||||||
case "CronJob":
|
|
||||||
return []string{"spec", "jobTemplate", "spec", "template", "spec"}
|
|
||||||
default:
|
|
||||||
return []string{"spec", "template", "spec"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func PodMetadata(kind string) []string {
|
|
||||||
switch kind {
|
|
||||||
case "Pod", "Namespace", "Secret":
|
|
||||||
return []string{"metadata"}
|
|
||||||
case "CronJob":
|
|
||||||
return []string{"spec", "jobTemplate", "spec", "template", "metadata"}
|
|
||||||
default:
|
|
||||||
return []string{"spec", "template", "metadata"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
const (
|
|
||||||
PostureRestAPIPathV1 = "/v1/posture"
|
|
||||||
PostureRedisPrefix = "_postureReportv1"
|
|
||||||
K8sPostureNotification = "/k8srestapi/v1/newPostureReport"
|
|
||||||
)
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
armotypes "github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AlertScore float32
|
|
||||||
type RuleLanguages string
|
|
||||||
|
|
||||||
const (
|
|
||||||
RegoLanguage RuleLanguages = "Rego"
|
|
||||||
RegoLanguage2 RuleLanguages = "rego"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegoResponse the expected response of single run of rego policy
|
|
||||||
type RuleResponse struct {
|
|
||||||
AlertMessage string `json:"alertMessage"`
|
|
||||||
RuleStatus string `json:"ruleStatus"`
|
|
||||||
PackageName string `json:"packagename"`
|
|
||||||
AlertScore AlertScore `json:"alertScore"`
|
|
||||||
AlertObject AlertObject `json:"alertObject"`
|
|
||||||
Context []string `json:"context,omitempty"` // TODO - Remove
|
|
||||||
Rulename string `json:"rulename,omitempty"` // TODO - Remove
|
|
||||||
ExceptionName string `json:"exceptionName,omitempty"` // Not in use
|
|
||||||
Exception *armotypes.PostureExceptionPolicy `json:"exception,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AlertObject struct {
|
|
||||||
K8SApiObjects []map[string]interface{} `json:"k8sApiObjects,omitempty"`
|
|
||||||
ExternalObjects map[string]interface{} `json:"externalObjects,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type FrameworkReport struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
ControlReports []ControlReport `json:"controlReports"`
|
|
||||||
Score float32 `json:"score,omitempty"`
|
|
||||||
ARMOImprovement float32 `json:"ARMOImprovement,omitempty"`
|
|
||||||
WCSScore float32 `json:"wcsScore,omitempty"`
|
|
||||||
}
|
|
||||||
type ControlReport struct {
|
|
||||||
armotypes.PortalBase `json:",inline"`
|
|
||||||
ControlID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
RuleReports []RuleReport `json:"ruleReports"`
|
|
||||||
Remediation string `json:"remediation"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Score float32 `json:"score,omitempty"`
|
|
||||||
BaseScore float32 `json:"baseScore,omitempty"`
|
|
||||||
ARMOImprovement float32 `json:"ARMOImprovement,omitempty"`
|
|
||||||
}
|
|
||||||
type RuleReport struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Remediation string `json:"remediation"`
|
|
||||||
RuleStatus RuleStatus `json:"ruleStatus"` // did we run the rule or not (if there where compile errors, the value will be failed)
|
|
||||||
RuleResponses []RuleResponse `json:"ruleResponses"`
|
|
||||||
ListInputResources []map[string]interface{} `json:"-"`
|
|
||||||
ListInputKinds []string `json:"-"`
|
|
||||||
}
|
|
||||||
type RuleStatus struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostureReport
|
|
||||||
type PostureReport struct {
|
|
||||||
CustomerGUID string `json:"customerGUID"`
|
|
||||||
ClusterName string `json:"clusterName"`
|
|
||||||
ReportID string `json:"reportID"`
|
|
||||||
JobID string `json:"jobID"`
|
|
||||||
ReportGenerationTime time.Time `json:"generationTime"`
|
|
||||||
FrameworkReports []FrameworkReport `json:"frameworks"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RuleMatchObjects defines which objects this rule applied on
|
|
||||||
type RuleMatchObjects struct {
|
|
||||||
APIGroups []string `json:"apiGroups"` // apps
|
|
||||||
APIVersions []string `json:"apiVersions"` // v1/ v1beta1 / *
|
|
||||||
Resources []string `json:"resources"` // dep.., pods,
|
|
||||||
}
|
|
||||||
|
|
||||||
// RuleMatchObjects defines which objects this rule applied on
|
|
||||||
type RuleDependency struct {
|
|
||||||
PackageName string `json:"packageName"` // package name
|
|
||||||
}
|
|
||||||
|
|
||||||
// PolicyRule represents single rule, the fundamental executable block of policy
|
|
||||||
type PolicyRule struct {
|
|
||||||
armotypes.PortalBase `json:",inline"`
|
|
||||||
CreationTime string `json:"creationTime"`
|
|
||||||
Rule string `json:"rule"` // multiline string!
|
|
||||||
RuleLanguage RuleLanguages `json:"ruleLanguage"`
|
|
||||||
Match []RuleMatchObjects `json:"match"`
|
|
||||||
RuleDependencies []RuleDependency `json:"ruleDependencies"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Remediation string `json:"remediation"`
|
|
||||||
RuleQuery string `json:"ruleQuery"` // default "armo_builtins" - DEPRECATED
|
|
||||||
}
|
|
||||||
|
|
||||||
// Control represents a collection of rules which are combined together to single purpose
|
|
||||||
type Control struct {
|
|
||||||
armotypes.PortalBase `json:",inline"`
|
|
||||||
ControlID string `json:"id"`
|
|
||||||
|
|
||||||
CreationTime string `json:"creationTime"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Remediation string `json:"remediation"`
|
|
||||||
Rules []PolicyRule `json:"rules"`
|
|
||||||
// for new list of rules in POST/UPADTE requests
|
|
||||||
RulesIDs *[]string `json:"rulesIDs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdatedControl struct {
|
|
||||||
Control `json:",inline"`
|
|
||||||
Rules []interface{} `json:"rules"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Framework represents a collection of controls which are combined together to expose comprehensive behavior
|
|
||||||
type Framework struct {
|
|
||||||
armotypes.PortalBase `json:",inline"`
|
|
||||||
CreationTime string `json:"creationTime"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Controls []Control `json:"controls"`
|
|
||||||
// for new list of controls in POST/UPADTE requests
|
|
||||||
ControlsIDs *[]string `json:"controlsIDs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdatedFramework struct {
|
|
||||||
Framework `json:",inline"`
|
|
||||||
Controls []interface{} `json:"controls"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type NotificationPolicyType string
|
|
||||||
type NotificationPolicyKind string
|
|
||||||
|
|
||||||
// Supported NotificationTypes
|
|
||||||
const (
|
|
||||||
TypeValidateRules NotificationPolicyType = "validateRules"
|
|
||||||
TypeExecPostureScan NotificationPolicyType = "execPostureScan"
|
|
||||||
TypeUpdateRules NotificationPolicyType = "updateRules"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Supported NotificationKinds
|
|
||||||
const (
|
|
||||||
KindFramework NotificationPolicyKind = "Framework"
|
|
||||||
KindControl NotificationPolicyKind = "Control"
|
|
||||||
KindRule NotificationPolicyKind = "Rule"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PolicyNotification struct {
|
|
||||||
NotificationType NotificationPolicyType `json:"notificationType"`
|
|
||||||
Rules []PolicyIdentifier `json:"rules"`
|
|
||||||
ReportID string `json:"reportID"`
|
|
||||||
JobID string `json:"jobID"`
|
|
||||||
Designators armotypes.PortalDesignator `json:"designators"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type PolicyIdentifier struct {
|
|
||||||
Kind NotificationPolicyKind `json:"kind"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
@@ -1,300 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
armotypes "github.com/armosec/kubescape/cautils/armotypes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Mock A
|
|
||||||
var (
|
|
||||||
AMockCustomerGUID = "5d817063-096f-4d91-b39b-8665240080af"
|
|
||||||
AMockJobID = "36b6f9e1-3b63-4628-994d-cbe16f81e9c7"
|
|
||||||
AMockReportID = "2c31e4da-c6fe-440d-9b8a-785b80c8576a"
|
|
||||||
AMockClusterName = "clusterA"
|
|
||||||
AMockFrameworkName = "testFrameworkA"
|
|
||||||
AMockControlName = "testControlA"
|
|
||||||
AMockRuleName = "testRuleA"
|
|
||||||
AMockPortalBase = *armotypes.MockPortalBase(AMockCustomerGUID, "", nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
func MockRuleResponseA() *RuleResponse {
|
|
||||||
return &RuleResponse{
|
|
||||||
AlertMessage: "test alert message A",
|
|
||||||
AlertScore: 0,
|
|
||||||
Rulename: AMockRuleName,
|
|
||||||
PackageName: "test.package.name.A",
|
|
||||||
Context: []string{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockFrameworkReportA() *FrameworkReport {
|
|
||||||
return &FrameworkReport{
|
|
||||||
Name: AMockFrameworkName,
|
|
||||||
ControlReports: []ControlReport{
|
|
||||||
{
|
|
||||||
Name: AMockControlName,
|
|
||||||
RuleReports: []RuleReport{
|
|
||||||
{
|
|
||||||
Name: AMockRuleName,
|
|
||||||
Remediation: "remove privilegedContainer: True flag from your pod spec",
|
|
||||||
RuleResponses: []RuleResponse{
|
|
||||||
*MockRuleResponseA(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockPostureReportA() *PostureReport {
|
|
||||||
return &PostureReport{
|
|
||||||
CustomerGUID: AMockCustomerGUID,
|
|
||||||
ClusterName: AMockClusterName,
|
|
||||||
ReportID: AMockReportID,
|
|
||||||
JobID: AMockJobID,
|
|
||||||
ReportGenerationTime: time.Now().UTC(),
|
|
||||||
FrameworkReports: []FrameworkReport{*MockFrameworkReportA()},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockFrameworkA() *Framework {
|
|
||||||
return &Framework{
|
|
||||||
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-096f-4d91-b39b-8665240080af", AMockFrameworkName, nil),
|
|
||||||
CreationTime: "",
|
|
||||||
Description: "mock framework descryption",
|
|
||||||
Controls: []Control{
|
|
||||||
{
|
|
||||||
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-4d91-b39b-8665240080af", AMockControlName, nil),
|
|
||||||
Rules: []PolicyRule{
|
|
||||||
*MockRuleA(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockRuleUntrustedRegistries() *PolicyRule {
|
|
||||||
return &PolicyRule{
|
|
||||||
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
|
|
||||||
Rule: `
|
|
||||||
package armo_builtins
|
|
||||||
# Check for images from blacklisted repos
|
|
||||||
|
|
||||||
untrusted_registries(z) = x {
|
|
||||||
x := ["015253967648.dkr.ecr.eu-central-1.amazonaws.com/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
public_registries(z) = y{
|
|
||||||
y := ["quay.io/kiali/","quay.io/datawire/","quay.io/keycloak/","quay.io/bitnami/"]
|
|
||||||
}
|
|
||||||
|
|
||||||
untrustedImageRepo[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
k := pod.kind
|
|
||||||
k == "Pod"
|
|
||||||
container := pod.spec.containers[_]
|
|
||||||
image := container.image
|
|
||||||
repo_prefix := untrusted_registries(image)[_]
|
|
||||||
startswith(image, repo_prefix)
|
|
||||||
selfLink := pod.metadata.selfLink
|
|
||||||
containerName := container.name
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("image '%v' in container '%s' in [%s] comes from untrusted registry", [image, containerName, selfLink]),
|
|
||||||
"alert": true,
|
|
||||||
"prevent": false,
|
|
||||||
"alertScore": 2,
|
|
||||||
"alertObject": [{"pod":pod}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
untrustedImageRepo[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
k := pod.kind
|
|
||||||
k == "Pod"
|
|
||||||
container := pod.spec.containers[_]
|
|
||||||
image := container.image
|
|
||||||
repo_prefix := public_registries(image)[_]
|
|
||||||
startswith(pod, repo_prefix)
|
|
||||||
selfLink := input.metadata.selfLink
|
|
||||||
containerName := container.name
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("image '%v' in container '%s' in [%s] comes from public registry", [image, containerName, selfLink]),
|
|
||||||
"alert": true,
|
|
||||||
"prevent": false,
|
|
||||||
"alertScore": 1,
|
|
||||||
"alertObject": [{"pod":pod}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
RuleLanguage: RegoLanguage,
|
|
||||||
Match: []RuleMatchObjects{
|
|
||||||
{
|
|
||||||
APIVersions: []string{"v1"},
|
|
||||||
APIGroups: []string{"*"},
|
|
||||||
Resources: []string{"pods"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RuleDependencies: []RuleDependency{
|
|
||||||
{
|
|
||||||
PackageName: "kubernetes.api.client",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockRuleA() *PolicyRule {
|
|
||||||
return &PolicyRule{
|
|
||||||
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
|
|
||||||
Rule: MockRegoPrivilegedPods(), //
|
|
||||||
RuleLanguage: RegoLanguage,
|
|
||||||
Match: []RuleMatchObjects{
|
|
||||||
{
|
|
||||||
APIVersions: []string{"v1"},
|
|
||||||
APIGroups: []string{"*"},
|
|
||||||
Resources: []string{"pods"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RuleDependencies: []RuleDependency{
|
|
||||||
{
|
|
||||||
PackageName: "kubernetes.api.client",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockRuleB() *PolicyRule {
|
|
||||||
return &PolicyRule{
|
|
||||||
PortalBase: *armotypes.MockPortalBase("bbbbbbbb-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
|
|
||||||
Rule: MockExternalFacingService(), //
|
|
||||||
RuleLanguage: RegoLanguage,
|
|
||||||
Match: []RuleMatchObjects{
|
|
||||||
{
|
|
||||||
APIVersions: []string{"v1"},
|
|
||||||
APIGroups: []string{""},
|
|
||||||
Resources: []string{"pods"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RuleDependencies: []RuleDependency{
|
|
||||||
{
|
|
||||||
PackageName: "kubernetes.api.client",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockPolicyNotificationA() *PolicyNotification {
|
|
||||||
return &PolicyNotification{
|
|
||||||
NotificationType: TypeExecPostureScan,
|
|
||||||
ReportID: AMockReportID,
|
|
||||||
JobID: AMockJobID,
|
|
||||||
Designators: armotypes.PortalDesignator{},
|
|
||||||
Rules: []PolicyIdentifier{
|
|
||||||
{
|
|
||||||
Kind: KindFramework,
|
|
||||||
Name: AMockFrameworkName,
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockTemp() string {
|
|
||||||
return `
|
|
||||||
package armo_builtins
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
deny[msga] {
|
|
||||||
#object := input[_]
|
|
||||||
object := client.query_all("pods")
|
|
||||||
obj := object.body.items[_]
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": "found object",
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": {"object": obj},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockRegoPrivilegedPods() string {
|
|
||||||
return `package armo_builtins
|
|
||||||
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
|
|
||||||
# Deny mutating action unless user is in group owning the resource
|
|
||||||
|
|
||||||
#privileged pods
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
pod := input[_]
|
|
||||||
containers := pod.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following pods are defined as privileged: %v", [pod]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": pod,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#handles majority of workload resources
|
|
||||||
deny[msga] {
|
|
||||||
wl := input[_]
|
|
||||||
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
|
|
||||||
spec_template_spec_patterns[wl.kind]
|
|
||||||
containers := wl.spec.template.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following workloads are defined as privileged: %v", [wl]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": wl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#handles cronjob
|
|
||||||
deny[msga] {
|
|
||||||
wl := input[_]
|
|
||||||
wl.kind == "CronJob"
|
|
||||||
containers := wl.spec.jobTemplate.spec.template.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following cronjobs are defined as privileged: %v", [wl]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": wl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
}
|
|
||||||
|
|
||||||
func MockExternalFacingService() string {
|
|
||||||
return "\n\tpackage armo_builtins\n\n\timport data.kubernetes.api.client as client\n\timport data.cautils as cautils\n\ndeny[msga] {\n\n\twl := input[_]\n\tcluster_resource := client.query_all(\n\t\t\"services\"\n\t)\n\n\tlabels := wl.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n#service := cluster_resource.body.items[i]\nservices := [svc | cluster_resource.body.items[i].metadata.namespace == wl.metadata.namespace; svc := cluster_resource.body.items[i]]\nservice := services[_]\nnp_or_lb := {\"NodePort\", \"LoadBalancer\"}\nnp_or_lb[service.spec.type]\ncautils.is_subobject(service.spec.selector,filtered_labels)\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v pod %v expose external facing service: %v\",[wl.metadata.namespace, wl.metadata.name, service.metadata.name]),\n\t\t\"alertScore\": 2,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\"srvc\":service}\n\t}\n}\n\t"
|
|
||||||
}
|
|
||||||
func GetRuntimePods() string {
|
|
||||||
return `
|
|
||||||
package armo_builtins
|
|
||||||
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
|
|
||||||
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
|
|
||||||
cluster_resource := client.query_all(
|
|
||||||
"pods"
|
|
||||||
)
|
|
||||||
|
|
||||||
pod := cluster_resource.body.items[i]
|
|
||||||
msga := {
|
|
||||||
"alertMessage": "got something",
|
|
||||||
"alertScore": 2,
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertObject": {"pod": pod}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
`
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMockPolicyNotificationA(t *testing.T) {
|
|
||||||
policy := MockPolicyNotificationA()
|
|
||||||
bp, err := json.Marshal(policy)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
} else {
|
|
||||||
t.Logf("%s\n", string(bp))
|
|
||||||
// t.Errorf("%s\n", string(bp))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMockFrameworkA(t *testing.T) {
|
|
||||||
policy := MockFrameworkA()
|
|
||||||
bp, err := json.Marshal(policy)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
} else {
|
|
||||||
t.Logf("%s\n", string(bp))
|
|
||||||
// t.Errorf("%s\n", string(bp))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMockPostureReportA(t *testing.T) {
|
|
||||||
policy := MockPostureReportA()
|
|
||||||
bp, err := json.Marshal(policy)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
} else {
|
|
||||||
// t.Errorf("%s\n", string(bp))
|
|
||||||
t.Logf("%s\n", string(bp))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (pn *PolicyNotification) ToJSONBytesBuffer() (*bytes.Buffer, error) {
|
|
||||||
res, err := json.Marshal(pn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return bytes.NewBuffer(res), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (RuleResponse *RuleResponse) GetSingleResultStatus() string {
|
|
||||||
if RuleResponse.Exception != nil {
|
|
||||||
if RuleResponse.Exception.IsAlertOnly() {
|
|
||||||
return "warning"
|
|
||||||
}
|
|
||||||
if RuleResponse.Exception.IsDisable() {
|
|
||||||
return "ignore"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "failed"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ruleReport *RuleReport) GetRuleStatus() (string, []RuleResponse, []RuleResponse) {
|
|
||||||
if len(ruleReport.RuleResponses) == 0 {
|
|
||||||
return "success", nil, nil
|
|
||||||
}
|
|
||||||
exceptions := make([]RuleResponse, 0)
|
|
||||||
failed := make([]RuleResponse, 0)
|
|
||||||
|
|
||||||
for _, rule := range ruleReport.RuleResponses {
|
|
||||||
if rule.ExceptionName != "" {
|
|
||||||
exceptions = append(exceptions, rule)
|
|
||||||
} else if rule.Exception != nil {
|
|
||||||
exceptions = append(exceptions, rule)
|
|
||||||
} else {
|
|
||||||
failed = append(failed, rule)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status := "failed"
|
|
||||||
if len(failed) == 0 && len(exceptions) > 0 {
|
|
||||||
status = "warning"
|
|
||||||
}
|
|
||||||
return status, failed, exceptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) GetNumberOfResources() int {
|
|
||||||
sum := 0
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
sum += controlReport.RuleReports[i].GetNumberOfResources()
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) GetNumberOfFailedResources() int {
|
|
||||||
sum := 0
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
sum += controlReport.RuleReports[i].GetNumberOfFailedResources()
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) GetNumberOfWarningResources() int {
|
|
||||||
sum := 0
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
sum += controlReport.RuleReports[i].GetNumberOfWarningResources()
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
func (controlReport *ControlReport) ListControlsInputKinds() []string {
|
|
||||||
listControlsInputKinds := []string{}
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
listControlsInputKinds = append(listControlsInputKinds, controlReport.RuleReports[i].ListInputKinds...)
|
|
||||||
}
|
|
||||||
return listControlsInputKinds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) Passed() bool {
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
if len(controlReport.RuleReports[i].RuleResponses) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) Warning() bool {
|
|
||||||
if controlReport.Passed() || controlReport.Failed() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
if status, _, _ := controlReport.RuleReports[i].GetRuleStatus(); status == "warning" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (controlReport *ControlReport) Failed() bool {
|
|
||||||
if controlReport.Passed() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i := range controlReport.RuleReports {
|
|
||||||
if status, _, _ := controlReport.RuleReports[i].GetRuleStatus(); status == "failed" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ruleReport *RuleReport) GetNumberOfResources() int {
|
|
||||||
return len(ruleReport.ListInputResources)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ruleReport *RuleReport) GetNumberOfFailedResources() int {
|
|
||||||
sum := 0
|
|
||||||
for i := range ruleReport.RuleResponses {
|
|
||||||
if ruleReport.RuleResponses[i].GetSingleResultStatus() == "failed" {
|
|
||||||
sum += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ruleReport *RuleReport) GetNumberOfWarningResources() int {
|
|
||||||
sum := 0
|
|
||||||
for i := range ruleReport.RuleResponses {
|
|
||||||
if ruleReport.RuleResponses[i].GetSingleResultStatus() == "warning" {
|
|
||||||
sum += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package opapolicy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/francoispqt/gojay"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
responsible on fast unmarshaling of various COMMON containerscan structures and substructures
|
|
||||||
|
|
||||||
*/
|
|
||||||
// UnmarshalJSONObject - File inside a pkg
|
|
||||||
func (r *PostureReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
|
|
||||||
|
|
||||||
switch key {
|
|
||||||
case "customerGUID":
|
|
||||||
err = dec.String(&(r.CustomerGUID))
|
|
||||||
|
|
||||||
case "clusterName":
|
|
||||||
err = dec.String(&(r.ClusterName))
|
|
||||||
|
|
||||||
case "reportID":
|
|
||||||
err = dec.String(&(r.ReportID))
|
|
||||||
case "jobID":
|
|
||||||
err = dec.String(&(r.JobID))
|
|
||||||
case "generationTime":
|
|
||||||
err = dec.Time(&(r.ReportGenerationTime), time.RFC3339)
|
|
||||||
r.ReportGenerationTime = r.ReportGenerationTime.Local()
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error {
|
|
||||||
// lae := PackageFile{}
|
|
||||||
// if err := dec.Object(&lae); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
|
|
||||||
// *files = append(*files, lae)
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (file *PostureReport) NKeys() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
//------------------------
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
package resources
|
|
||||||
|
|
||||||
var RegoCAUtils = `
|
|
||||||
package cautils
|
|
||||||
|
|
||||||
list_contains(lista,element) {
|
|
||||||
some i
|
|
||||||
lista[i] == element
|
|
||||||
}
|
|
||||||
|
|
||||||
# getPodName(metadata) = name {
|
|
||||||
# name := metadata.generateName
|
|
||||||
#}
|
|
||||||
getPodName(metadata) = name {
|
|
||||||
name := metadata.name
|
|
||||||
}
|
|
||||||
|
|
||||||
#returns subobject ,sub1 is partial to parent, e.g parent = {a:a,b:b,c:c,d:d}
|
|
||||||
# sub1 = {b:b,c:c} - result is {b:b,c:c}, if sub1={b:b,e:f} returns {b:b}
|
|
||||||
object_intersection(parent,sub1) = r{
|
|
||||||
|
|
||||||
r := {k:p | p := sub1[k]
|
|
||||||
parent[k]== p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#returns if parent contains sub(both are objects not sets!!)
|
|
||||||
is_subobject(sub,parent) {
|
|
||||||
object_intersection(sub,parent) == sub
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
var RegoDesignators = `
|
|
||||||
package designators
|
|
||||||
|
|
||||||
import data.cautils
|
|
||||||
#functions that related to designators
|
|
||||||
|
|
||||||
#allowed_namespace
|
|
||||||
#@input@: receive as part of the input object "included_namespaces" list
|
|
||||||
#@input@: item's namespace as "namespace"
|
|
||||||
#returns true if namespace exists in that list
|
|
||||||
included_namespaces(namespace){
|
|
||||||
cautils.list_contains(["default"],namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
#forbidden_namespaces
|
|
||||||
#@input@: receive as part of the input object "forbidden_namespaces" list
|
|
||||||
#@input@: item's namespace as "namespace"
|
|
||||||
#returns true if namespace exists in that list
|
|
||||||
excluded_namespaces(namespace){
|
|
||||||
not cautils.list_contains(["excluded"],namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
forbidden_wlids(wlid){
|
|
||||||
input.forbidden_wlids[_] == wlid
|
|
||||||
}
|
|
||||||
|
|
||||||
filter_k8s_object(obj) = filtered {
|
|
||||||
#put
|
|
||||||
filtered := obj
|
|
||||||
#filtered := [ x | cautils.list_contains(["default"],obj[i].metadata.namespace) ; x := obj[i] ]
|
|
||||||
# filtered := [ x | not cautils.list_contains([],filter1Set[i].metadata.namespace); x := filter1Set[i]]
|
|
||||||
|
|
||||||
}
|
|
||||||
`
|
|
||||||
var RegoKubernetesApiClient = `
|
|
||||||
package kubernetes.api.client
|
|
||||||
|
|
||||||
# service account token
|
|
||||||
token := data.k8sconfig.token
|
|
||||||
|
|
||||||
# Cluster host
|
|
||||||
host := data.k8sconfig.host
|
|
||||||
|
|
||||||
# default certificate path
|
|
||||||
# crt_file := "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
|
|
||||||
crt_file := data.k8sconfig.crtfile
|
|
||||||
|
|
||||||
client_crt_file := data.k8sconfig.clientcrtfile
|
|
||||||
client_key_file := data.k8sconfig.clientkeyfile
|
|
||||||
|
|
||||||
|
|
||||||
# This information could be retrieved from the kubernetes API
|
|
||||||
# too, but would essentially require a request per API group,
|
|
||||||
# so for now use a lookup table for the most common resources.
|
|
||||||
resource_group_mapping := {
|
|
||||||
"services": "api/v1",
|
|
||||||
"pods": "api/v1",
|
|
||||||
"configmaps": "api/v1",
|
|
||||||
"secrets": "api/v1",
|
|
||||||
"persistentvolumeclaims": "api/v1",
|
|
||||||
"daemonsets": "apis/apps/v1",
|
|
||||||
"deployments": "apis/apps/v1",
|
|
||||||
"statefulsets": "apis/apps/v1",
|
|
||||||
"horizontalpodautoscalers": "api/autoscaling/v1",
|
|
||||||
"jobs": "apis/batch/v1",
|
|
||||||
"cronjobs": "apis/batch/v1beta1",
|
|
||||||
"ingresses": "api/extensions/v1beta1",
|
|
||||||
"replicasets": "apis/apps/v1",
|
|
||||||
"networkpolicies": "apis/networking.k8s.io/v1",
|
|
||||||
"clusterroles": "apis/rbac.authorization.k8s.io/v1",
|
|
||||||
"clusterrolebindings": "apis/rbac.authorization.k8s.io/v1",
|
|
||||||
"roles": "apis/rbac.authorization.k8s.io/v1",
|
|
||||||
"rolebindings": "apis/rbac.authorization.k8s.io/v1",
|
|
||||||
"serviceaccounts": "api/v1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Query for given resource/name in provided namespace
|
|
||||||
# Example: query_ns("deployments", "my-app", "default")
|
|
||||||
query_name_ns(resource, name, namespace) = http.send({
|
|
||||||
"url": sprintf("%v/%v/namespaces/%v/%v/%v", [
|
|
||||||
host,
|
|
||||||
resource_group_mapping[resource],
|
|
||||||
namespace,
|
|
||||||
resource,
|
|
||||||
name,
|
|
||||||
]),
|
|
||||||
"method": "get",
|
|
||||||
"headers": {"authorization": token},
|
|
||||||
"tls_client_cert_file": client_crt_file,
|
|
||||||
"tls_client_key_file": client_key_file,
|
|
||||||
"tls_ca_cert_file": crt_file,
|
|
||||||
"raise_error": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
# Query for given resource type using label selectors
|
|
||||||
# https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
|
|
||||||
# Example: query_label_selector_ns("deployments", {"app": "opa-kubernetes-api-client"}, "default")
|
|
||||||
query_label_selector_ns(resource, selector, namespace) = http.send({
|
|
||||||
"url": sprintf("%v/%v/namespaces/%v/%v?labelSelector=%v", [
|
|
||||||
host,
|
|
||||||
resource_group_mapping[resource],
|
|
||||||
namespace,
|
|
||||||
resource,
|
|
||||||
label_map_to_query_string(selector),
|
|
||||||
]),
|
|
||||||
"method": "get",
|
|
||||||
"headers": {"authorization": token},
|
|
||||||
"tls_client_cert_file": client_crt_file,
|
|
||||||
"tls_client_key_file": client_key_file,
|
|
||||||
"tls_ca_cert_file": crt_file,
|
|
||||||
"raise_error": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
# x := field_transform_to_qry_param("spec.selector",input)
|
|
||||||
# input = {"app": "acmefit", "service": "catalog-db"}
|
|
||||||
# result: "spec.selector.app%3Dacmefit,spec.selector.service%3Dcatalog-db"
|
|
||||||
|
|
||||||
|
|
||||||
query_field_selector_ns(resource, field, selector, namespace) = http.send({
|
|
||||||
"url": sprintf("%v/%v/namespaces/%v/%v?fieldSelector=%v", [
|
|
||||||
host,
|
|
||||||
resource_group_mapping[resource],
|
|
||||||
namespace,
|
|
||||||
resource,
|
|
||||||
field_transform_to_qry_param(field,selector),
|
|
||||||
]),
|
|
||||||
"method": "get",
|
|
||||||
"headers": {"authorization": token},
|
|
||||||
"tls_client_cert_file": client_crt_file,
|
|
||||||
"tls_client_key_file": client_key_file,
|
|
||||||
"tls_ca_cert_file": crt_file,
|
|
||||||
"raise_error": true,
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
# # Query for all resources of type resource in all namespaces
|
|
||||||
# # Example: query_all("deployments")
|
|
||||||
# query_all(resource) = http.send({
|
|
||||||
# "url": sprintf("https://%v:%v/%v/%v", [
|
|
||||||
# ip,
|
|
||||||
# port,
|
|
||||||
# resource_group_mapping[resource],
|
|
||||||
# resource,
|
|
||||||
# ]),
|
|
||||||
# "method": "get",
|
|
||||||
# "headers": {"authorization": sprintf("Bearer %v", [token])},
|
|
||||||
# "tls_client_cert_file": crt_file,
|
|
||||||
# "raise_error": true,
|
|
||||||
# })
|
|
||||||
|
|
||||||
# Query for all resources of type resource in all namespaces
|
|
||||||
# Example: query_all("deployments")
|
|
||||||
query_all(resource) = http.send({
|
|
||||||
"url": sprintf("%v/%v/%v", [
|
|
||||||
host,
|
|
||||||
resource_group_mapping[resource],
|
|
||||||
resource,
|
|
||||||
]),
|
|
||||||
"method": "get",
|
|
||||||
"headers": {"authorization": token},
|
|
||||||
"tls_client_cert_file": client_crt_file,
|
|
||||||
"tls_client_key_file": client_key_file,
|
|
||||||
"tls_ca_cert_file": crt_file,
|
|
||||||
"raise_error": true,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Query for all resources of type resource in all namespaces - without authentication
|
|
||||||
# Example: query_all("deployments")
|
|
||||||
query_all_no_auth(resource) = http.send({
|
|
||||||
"url": sprintf("%v/%v/namespaces/default/%v", [
|
|
||||||
host,
|
|
||||||
resource_group_mapping[resource],
|
|
||||||
resource,
|
|
||||||
]),
|
|
||||||
"method": "get",
|
|
||||||
"raise_error": true,
|
|
||||||
"tls_insecure_skip_verify" : true,
|
|
||||||
})
|
|
||||||
|
|
||||||
field_transform_to_qry_param(field,map) = finala {
|
|
||||||
mid := {concat(".",[field,key]): val | val := map[key]}
|
|
||||||
finala := label_map_to_query_string(mid)
|
|
||||||
}
|
|
||||||
label_map_to_query_string(map) = concat(",", [str | val := map[key]; str := concat("%3D", [key, val])])
|
|
||||||
`
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
|
|
||||||
# import data.kubernetes.api.client as client
|
|
||||||
import data.cautils as cautils
|
|
||||||
|
|
||||||
|
|
||||||
# alert cronjobs
|
|
||||||
|
|
||||||
#handles cronjob
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
wl := input[_]
|
|
||||||
wl.kind == "CronJob"
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("the following cronjobs are defined: %v", [wl]),
|
|
||||||
"alertScore": 2,
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertObject": wl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
|
|
||||||
|
|
||||||
# input: pod
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns the external facing services of that pod
|
|
||||||
#
|
|
||||||
#
|
|
||||||
deny[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
podns := pod.metadata.namespace
|
|
||||||
podname := getName(pod.metadata)
|
|
||||||
# pod := client.query_name_ns("pods","frontend-86c5ffb485-kfp9d", "default")
|
|
||||||
labels := pod.body.metadata.labels
|
|
||||||
filtered_labels := json.remove(labels, ["pod-template-hash"])
|
|
||||||
|
|
||||||
cluster_resource := client.query_all(
|
|
||||||
"services"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
services := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]
|
|
||||||
service := services[_]
|
|
||||||
np_or_lb := {"NodePort", "LoadBalancer"}
|
|
||||||
np_or_lb[service.spec.type]
|
|
||||||
service.spec.selector == filtered_labels
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("pod %v/%v exposed services: %v\n", [podns,podname,service]),
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": {"service":service,"labels":filtered_labels, "podname":podname,"namespace":podns}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getName(metadata) = name {
|
|
||||||
name := metadata.generateName
|
|
||||||
}
|
|
||||||
getName(metadata) = name {
|
|
||||||
name := metadata.name
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
#import data.kubernetes.api.client as client
|
|
||||||
import data.cautils as cautils
|
|
||||||
|
|
||||||
# input: pod
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns hostPath volumes
|
|
||||||
#
|
|
||||||
#
|
|
||||||
deny[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
pod.kind == "Pod"
|
|
||||||
volumes := pod.spec.volumes
|
|
||||||
volume := volumes[_]
|
|
||||||
# crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name
|
|
||||||
volume.hostPath
|
|
||||||
podname := cautils.getPodName(pod.metadata)
|
|
||||||
obj := {"volume":volume,"podname": podname}
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("pod: %v has {%v,%v} ashostPath volume \n\n\n", [podname, volume]),
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": [obj]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isRWMount(mount) {
|
|
||||||
not mount.readOnly
|
|
||||||
}
|
|
||||||
isRWMount(mount) {
|
|
||||||
mount.readOnly == false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#handles majority of workload resources
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
wl := input[_]
|
|
||||||
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
|
|
||||||
spec_template_spec_patterns[wl.kind]
|
|
||||||
volumes := wl.spec.template.spec.volumes
|
|
||||||
volume := volumes[_]
|
|
||||||
volume.hostPath
|
|
||||||
wlname := cautils.getPodName(wl.metadata)
|
|
||||||
obj := {"volume":volume,"podname": wlname}
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("%v: %v has {%v,%v} as hostPath volume\n\n\n", [wl.kind,wlname, volume]),
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": [obj]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
|
|
||||||
#import data.kubernetes.api.client as client
|
|
||||||
|
|
||||||
|
|
||||||
# Deny mutating action unless user is in group owning the resource
|
|
||||||
|
|
||||||
|
|
||||||
#privileged pods
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
|
|
||||||
pod := input[_]
|
|
||||||
containers := pod.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following pods are defined as privileged: %v", [pod]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": pod,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#handles majority of workload resources
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
wl := input[_]
|
|
||||||
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
|
|
||||||
spec_template_spec_patterns[wl.kind]
|
|
||||||
containers := wl.spec.template.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following workloads are defined as privileged: %v", [wl]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": wl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#handles cronjob
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
wl := input[_]
|
|
||||||
wl.kind == "CronJob"
|
|
||||||
containers := wl.spec.jobTemplate.spec.template.spec.containers[_]
|
|
||||||
containers.securityContext.privileged == true
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("the following cronjobs are defined as privileged: %v", [wl]),
|
|
||||||
"alertScore": 3,
|
|
||||||
"alertObject": wl,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
import data.cautils as cautils
|
|
||||||
|
|
||||||
|
|
||||||
# input: None
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns roles+ related subjects in rolebinding
|
|
||||||
|
|
||||||
|
|
||||||
deny[msga] {
|
|
||||||
# rsrc := client.query_all("roles")
|
|
||||||
# role := rsrc.body.items[_]
|
|
||||||
role := input[_]
|
|
||||||
role.kind == "Role"
|
|
||||||
rule := role.rules[_]
|
|
||||||
cautils.list_contains(rule.resources,"secrets")
|
|
||||||
canViewSecrets(rule)
|
|
||||||
rbsrc := client.query_all("rolebindings")
|
|
||||||
rolebinding := rbsrc.body.items[_]
|
|
||||||
rolebinding.roleRef.kind == "Role"
|
|
||||||
rolebinding.roleRef.name == role.metadata.name
|
|
||||||
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
|
|
||||||
"alertScore": 9,
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertObject": {"role":role,"users":rolebinding.subjects}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# input: None
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns clusterroles+ related subjects in rolebinding
|
|
||||||
|
|
||||||
|
|
||||||
deny[msga] {
|
|
||||||
# rsrc := client.query_all("clusterroles")
|
|
||||||
# role := rsrc.body.items[_]
|
|
||||||
role := input[_]
|
|
||||||
role.kind == "ClusterRole"
|
|
||||||
rule := role.rules[_]
|
|
||||||
cautils.list_contains(rule.resources,"secrets")
|
|
||||||
canViewSecrets(rule)
|
|
||||||
rbsrc := client.query_all("rolebindings")
|
|
||||||
rolebinding := rbsrc.body.items[_]
|
|
||||||
rolebinding.roleRef.kind == "ClusterRole"
|
|
||||||
rolebinding.roleRef.name == role.metadata.name
|
|
||||||
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
|
|
||||||
"alertScore": 9,
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertObject": {"clusterrole":role,"users":rolebinding.subjects}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# input: None
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns clusterroles+ related subjects in clusterrolebinding
|
|
||||||
#
|
|
||||||
#
|
|
||||||
deny[msga] {
|
|
||||||
# rsrc := client.query_all("clusterroles")
|
|
||||||
# role := rsrc.body.items[_]
|
|
||||||
role := input[_]
|
|
||||||
role.kind == "ClusterRole"
|
|
||||||
rule := role.rules[_]
|
|
||||||
cautils.list_contains(rule.resources,"secrets")
|
|
||||||
canViewSecrets(rule)
|
|
||||||
rbsrc := client.query_all("clusterrolebindings")
|
|
||||||
rolebinding := rbsrc.body.items[_]
|
|
||||||
rolebinding.roleRef.kind == "ClusterRole"
|
|
||||||
rolebinding.roleRef.name == role.metadata.name
|
|
||||||
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
|
|
||||||
"alertScore": 9,
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertObject": {"clusterrole":role,"users":rolebinding.subjects}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canViewSecrets(rule) {
|
|
||||||
cautils.list_contains(rule.verbs,"get")
|
|
||||||
}
|
|
||||||
canViewSecrets(rule) {
|
|
||||||
cautils.list_contains(rule.verbs,"watch")
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
#import data.kubernetes.api.client as client
|
|
||||||
import data.cautils as cautils
|
|
||||||
|
|
||||||
# input: pod
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns rw hostpath volumes of that pod
|
|
||||||
#
|
|
||||||
#
|
|
||||||
deny[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
pod.kind == "Pod"
|
|
||||||
volumes := pod.spec.volumes
|
|
||||||
volume := volumes[_]
|
|
||||||
# crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name
|
|
||||||
mount := pod.spec.containers[_].volumeMounts[_]
|
|
||||||
mount.name == volume.name
|
|
||||||
volume.hostPath
|
|
||||||
isRWMount(mount)
|
|
||||||
podname := cautils.getPodName(pod.metadata)
|
|
||||||
obj := {"volume":volume,"mount":mount,"podname": podname}
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("pod: %v has {%v,%v} as rw hostPath volume and volumemount pair\n\n\n", [podname, volume,mount]),
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": [obj],
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isRWMount(mount) {
|
|
||||||
not mount.readOnly
|
|
||||||
}
|
|
||||||
isRWMount(mount) {
|
|
||||||
mount.readOnly == false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#handles majority of workload resources
|
|
||||||
deny[msga] {
|
|
||||||
|
|
||||||
wl := input[_]
|
|
||||||
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
|
|
||||||
spec_template_spec_patterns[wl.kind]
|
|
||||||
volumes := wl.spec.template.spec.volumes
|
|
||||||
volume := volumes[_]
|
|
||||||
mount := wl.spec.template.spec.containers[_].volumeMounts[_]
|
|
||||||
mount.name == volume.name
|
|
||||||
volume.hostPath
|
|
||||||
isRWMount(mount)
|
|
||||||
wlname := cautils.getPodName(wl.metadata)
|
|
||||||
obj := {"volume":volume,"mount":mount,"podname": wlname}
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertMessage": sprintf("%v: %v has {%v,%v} as rw hostPath volume and volumemount pair\n\n\n", [wl.kind,wlname, volume,mount]),
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": [obj],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package armo_builtins
|
|
||||||
import data.kubernetes.api.client as client
|
|
||||||
import data.cautils as cautils
|
|
||||||
|
|
||||||
# input: pod
|
|
||||||
# apiversion: v1
|
|
||||||
# does:
|
|
||||||
# returns the external facing services of that pod
|
|
||||||
#
|
|
||||||
#
|
|
||||||
deny[msga] {
|
|
||||||
pod := input[_]
|
|
||||||
podns := pod.metadata.namespace
|
|
||||||
podname := cautils.getPodName(pod.metadata)
|
|
||||||
# pod := client.query_name_ns("pods", "catalog-mongo-6f468d99b4-pn242", "default")
|
|
||||||
labels := pod.body.metadata.labels
|
|
||||||
filtered_labels := json.remove(labels, ["pod-template-hash"])
|
|
||||||
|
|
||||||
cluster_resource := client.query_all(
|
|
||||||
"services"
|
|
||||||
)
|
|
||||||
|
|
||||||
services := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]
|
|
||||||
service := services[_]
|
|
||||||
service.spec.selector == filtered_labels
|
|
||||||
|
|
||||||
hasSSHPorts(service)
|
|
||||||
|
|
||||||
msga := {
|
|
||||||
"alertMessage": sprintf("pod %v/%v exposed by SSH services: %v\n", [podns,podname,service]),
|
|
||||||
"packagename": "armo_builtins",
|
|
||||||
"alertScore": 7,
|
|
||||||
"alertObject": [{"pod":pod,"service":{service}}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hasSSHPorts(service) {
|
|
||||||
port := service.spec.ports[_]
|
|
||||||
port.port == 22
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hasSSHPorts(service) {
|
|
||||||
port := service.spec.ports[_]
|
|
||||||
port.port == 2222
|
|
||||||
}
|
|
||||||
|
|
||||||
hasSSHPorts(service) {
|
|
||||||
port := service.spec.ports[_]
|
|
||||||
port.targetPort == 22
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
hasSSHPorts(service) {
|
|
||||||
port := service.spec.ports[_]
|
|
||||||
port.targetPort == 2222
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "[Builtin] rule-deny-access-to-secrets",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "determines which users can get/list/watch secrets",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Credential Access::List k8s Secrets"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"cautils"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"packageName":"kubernetes.api.client"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Role","ClusterRole"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"rbac.authorization.k8s.io"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns roles+ related subjects in rolebinding\n\n\ndeny[msga] {\n\t# rsrc := client.query_all(\"roles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"Role\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"rolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"Role\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"role\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns clusterroles+ related subjects in rolebinding\n\n\ndeny[msga] {\n\t# rsrc := client.query_all(\"clusterroles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"ClusterRole\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"rolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"clusterrole\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns clusterroles+ related subjects in clusterrolebinding\n#\n#\ndeny[msga] {\n\t# rsrc := client.query_all(\"clusterroles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"ClusterRole\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"clusterrolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"clusterrole\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\ncanViewSecrets(rule) {\n\tcautils.list_contains(rule.verbs,\"get\")\n}\ncanViewSecrets(rule) {\n\tcautils.list_contains(rule.verbs,\"watch\")\n}\n"
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "[Builtin] rule-can-ssh-to-pod",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "denies pods with SSH ports opened(22/222)",
|
|
||||||
"attributes": {
|
|
||||||
"microsoftK8sThreatMatrix": "val1"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"cautils"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"packageName":"kubernetes.api.client"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "create a network policy that protects SSH ports",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Pods"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns the external facing services of that pod\n#\n#\ndeny[msga] {\n\tpod := input[_]\n\tpodns := pod.metadata.namespace\n\tpodname := cautils.getPodName(pod.metadata)\n\t# pod := client.query_name_ns(\"pods\", \"catalog-mongo-6f468d99b4-pn242\", \"default\")\n\tlabels := pod.body.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n\t cluster_resource := client.query_all(\n\t \t\"services\"\n\t )\n\n\tservices := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]\n\tservice := \tservices[_]\n\tservice.spec.selector == filtered_labels\n \n\thasSSHPorts(service)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod %v/%v exposed by SSH services: %v\n\", [podns,podname,service]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [{\"pod\":pod,\"service\":{service}}]\n\t\n\t}\n}\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.port == 22\n}\n\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.port == 2222\n}\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.targetPort == 22\n}\n\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.targetPort == 2222\n}\n"
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "",
|
|
||||||
"name": "[Builtin] rule-identify-blacklisted-image-registries",
|
|
||||||
"creationTime": "",
|
|
||||||
"description": "Identifying if pod container images are from unallowed registries",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Initial Access::Compromised images in registry"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName": "cautils"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"packageName": "kubernetes.api.client"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "Use images from safe registry",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Pods"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\n# Check for images from blacklisted repos\n\nuntrusted_registries(z) = x {\n\tx := [\"015253967648.dkr.ecr.eu-central-1.amazonaws.com/\"]\t\n}\n\npublic_registries(z) = y{\n\ty := [\"quay.io/kiali/\",\"quay.io/datawire/\",\"quay.io/keycloak/\",\"quay.io/bitnami/\"]\n}\n\nuntrustedImageRepo[msga] {\n\tpod := input[_]\n\tk := pod.kind\n\tk == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n\timage := container.image\n repo_prefix := untrusted_registries(image)[_]\n\tstartswith(image, repo_prefix)\n\tselfLink := pod.metadata.selfLink\n\tcontainerName := container.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' in [%s] comes from untrusted registry\", [image, containerName, selfLink]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 2,\n\t\t\"alertObject\": [{\"pod\":pod}]\n\t}\n}\n\nuntrustedImageRepo[msga] {\n pod := input[_]\n\tk := pod.kind\n\tk == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n\timage := container.image\n repo_prefix := public_registries(image)[_]\n\tstartswith(pod, repo_prefix)\n\tselfLink := input.metadata.selfLink\n\tcontainerName := container.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' in [%s] comes from public registry\", [image, containerName, selfLink]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"pod\":pod}]\n\t}\n}"
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "[Builtin] rule-pod-external-facing",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "denies pods with external facing services, grabs related services",
|
|
||||||
"attributes": {
|
|
||||||
"microsoftK8sThreatMatrix": "val1"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"kubernetes.api.client"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "create a network policy that controls which protect your cluster from unwanted connections and the outside world",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Pods"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\n\nimport data.kubernetes.api.client as client\n\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns the external facing services of that pod\n#\n#\ndeny[msga] {\n\tpod := input[_]\n\tpodns := pod.metadata.namespace\n\tpodname := getName(pod.metadata)\n\t# pod := client.query_name_ns(\"pods\",\"frontend-86c5ffb485-kfp9d\", \"default\")\n\tlabels := pod.body.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n\t cluster_resource := client.query_all(\n\t \t\"services\"\n\t )\n\n\n\tservices := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]\n\tservice := \tservices[_]\n\tnp_or_lb := {\"NodePort\", \"LoadBalancer\"}\n\tnp_or_lb[service.spec.type]\n\tservice.spec.selector == filtered_labels\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod %v/%v exposed services: %v\n\", [podns,podname,service]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\"service\":service,\"labels\":filtered_labels, \"podname\":podname,\"namespace\":podns}\n\t\n\t}\n}\n\ngetName(metadata) = name {\n\tname := metadata.generateName\n}\ngetName(metadata) = name {\n\tname := metadata.name\n}\n"
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "[Builtin] alert-any-hostpath",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "determines if any workload contains a hostPath volume",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Privilege Escalation::hostPath mount"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"cautils"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "consider if hostPath is really necessary - reading sensitive data like hostPath credentials might endanger cluster, if so consider encrypting the data",
|
|
||||||
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns hostPath volumes\n#\n#\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n # crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name\n volume.hostPath\n podname := cautils.getPodName(pod.metadata)\n obj := {\"volume\":volume,\"podname\": podname}\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has {%v,%v} ashostPath volume \n\n\n\", [podname, volume]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [obj],\n\t\n\t}\n}\n\nisRWMount(mount) {\n not mount.readOnly\n}\nisRWMount(mount) {\n mount.readOnly == false\n}\n\n\n#handles majority of workload resources\ndeny[msga] {\n\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n wlname := cautils.getPodName(wl.metadata)\n obj := {\"volume\":volume,\"podname\": wlname}\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has {%v,%v} as hostPath volume\n\n\n\", [wl.kind,wlname, volume]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [obj],\n\t\n\t}\n}\n\n\n"
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "82f19070-2826-4fe4-a079-f5f7e7a1b04d",
|
|
||||||
"name": "[Builtin] instance-metadata-api-access",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Credential Access::Instance Metadata API"
|
|
||||||
},
|
|
||||||
"creationTime": "2021-04-25T10:48:48.861806",
|
|
||||||
"rule": "package armo_builtins\n# Check for images from blacklisted repos\n\nmetadata_azure(z) = http.send({\n\t\"url\": \"http://169.254.169.254/metadata/instance?api-version=2020-09-01\",\n\t\"method\": \"get\",\n\t\"headers\": {\"Metadata\": \"true\"},\n\t\"raise_error\": true,\t\n})\n\nmetadata_gcp(z) = http.send({\n\t\"url\": \"http://169.254.169.254/computeMetadata/v1/?alt=json&recursive=true\",\n\t\"method\": \"get\",\n\t\"headers\": {\"Metadata-Flavor\": \"Google\"},\n\t\"raise_error\": true,\t\n})\n\nmetadata_aws(z) = metadata_object { \n\thostname := http.send({\n\t\"url\": \"http://169.254.169.254/latest/meta-data/local-hostname\",\n\t\"method\": \"get\",\n\t\"raise_error\": true,\t\n })\n\tmetadata_object := {\n\t\t\"raw_body\": hostname.raw_body,\n\t\t\"hostname\" : hostname.raw_body,\n\t\t\"status_code\" : hostname.status_code\n\t}\n}\n\nazure_metadata[msga] {\t\n\tmetadata_object := metadata_azure(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.body.compute.name\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of Azure.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\":metadata_object.body}]\n\t}\n}\n\ngcp_metadata[msga] {\t\n\tmetadata_object := metadata_gcp(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.body.instance.hostname\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of GCP.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\": metadata_object.raw_body}]\n\t}\n}\n\naws_metadata[msga] {\t\n\tmetadata_object := metadata_aws(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.hostname\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of AWS.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\": metadata_object.raw_body}]\n\t}\n}",
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"*"
|
|
||||||
],
|
|
||||||
"resources": [
|
|
||||||
"nodes"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleDependencies": [],
|
|
||||||
"description": "Checks if there is access from the nodes to cloud prividers instance metadata services",
|
|
||||||
"remediation": "From https://attack.mitre.org/techniques/T1552/005/ :Option A: Disable or Remove Feature or Program, Option B: Filter Network Traffic",
|
|
||||||
"ruleQuery": ""
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "[Builtin] 3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "rule-deny-cronjobs",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "determines if it's cronjob",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Persistence::Cronjob"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"cautils"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"CronJob"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1beta1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"batch"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\n\n# import data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n\n# alert cronjobs\n\n#handles cronjob\ndeny[msga] {\n\n\twl := input[_]\n\twl.kind == \"CronJob\"\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following cronjobs are defined: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 2,\n\t\t\"alertObject\": wl\n\t\n\t}\n}\n"
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "",
|
|
||||||
"name": "[Builtin] rule-privilege-escalation",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "determines if pods/deployments defined as privileged true",
|
|
||||||
"attributes": {
|
|
||||||
"mitre": "Privilege Escalation",
|
|
||||||
"mitreCode": "TA0004",
|
|
||||||
"m$K8sThreatMatrix": "Privilege Escalation::privileged container"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
],
|
|
||||||
"remediation": "avoid defining pods as privilleged",
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\npackage armo_builtins\n\nimport data.kubernetes.api.client as client\nimport data.designators as scope\nimport data.cautils as cautils\n\n\n# Deny mutating action unless user is in group owning the resource\n\n\n#privileged pods\ndeny[msga] {\n\n \n\tpod := input[_]\n\tcontainers := pod.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following pods are defined as privileged: %v\", [pod]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": pod,\n\t\n\t}\n}\n\n\n#handles majority of workload resources\ndeny[msga] {\n\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainers := wl.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following workloads are defined as privileged: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": wl,\n\t\n\t}\n}\n\n\n\n#handles cronjob\ndeny[msga] {\n\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainers := wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following cronjobs are defined as privileged: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": wl,\n\t\n\t}\n}\n\n"
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
|
|
||||||
"name": "[Builtin] alert-rw-hostpath",
|
|
||||||
"creationTime": "2019-09-04T12:04:58.461455",
|
|
||||||
"description": "determines if any workload contains a hostPath volume with rw permissions",
|
|
||||||
"attributes": {
|
|
||||||
"m$K8sThreatMatrix": "Persistance::Writable hostPath mount"
|
|
||||||
},
|
|
||||||
"ruleDependencies": [
|
|
||||||
{
|
|
||||||
"packageName":"cautils"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"remediation": "consider if hostPath is really necessary- sensitive data like hostPath credentials might endanger cluster, if so consider encrypting the data",
|
|
||||||
|
|
||||||
"match": [
|
|
||||||
{
|
|
||||||
"resources": [
|
|
||||||
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod"
|
|
||||||
],
|
|
||||||
"apiVersions": [
|
|
||||||
"v1"
|
|
||||||
],
|
|
||||||
"apiGroups": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ruleLanguage": "Rego",
|
|
||||||
"rule": "\"\\npackage armo_builtins\\nimport data.kubernetes.api.client as client\\nimport data.cautils as cautils\\n\\n# input: pod\\n# apiversion: v1\\n# does: \\n#\\treturns hostPath volumes\\n#\\n#\\ndeny[msga] {\\n pod := input[_]\\n pod.kind == \\\"Pod\\\"\\n volumes := pod.spec.volumes\\n volume := volumes[_]\\n # crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name\\n volume.hostPath\\n podname := cautils.getPodName(pod.metadata)\\n obj := {\\\"volume\\\":volume,\\\"podname\\\": podname}\\n\\n\\tmsga := {\\n\\t\\t\\\"alertMessage\\\": sprintf(\\\"pod: %v has {%v,%v} ashostPath volume \\n\\n\\n\\\", [podname, volume]),\\n\\t\\t\\\"alert\\\": true,\\n\\t\\t\\\"prevent\\\": false,\\n\\t\\t\\\"alertScore\\\": 7,\\n\\t\\t\\\"alertObject\\\": [obj],\\n\\t\\n\\t}\\n}\\n\\nisRWMount(mount) {\\n not mount.readOnly\\n}\\nisRWMount(mount) {\\n mount.readOnly == false\\n}\\n\\n\\n#handles majority of workload resources\\ndeny[msga] {\\n\\n\\twl := input[_]\\n\\tspec_template_spec_patterns := {\\\"Deployment\\\",\\\"ReplicaSet\\\",\\\"DaemonSet\\\",\\\"StatefulSet\\\",\\\"Job\\\"}\\n\\tspec_template_spec_patterns[wl.kind]\\n volumes := wl.spec.template.spec.volumes\\n volume := volumes[_]\\n volume.hostPath\\n wlname := cautils.getPodName(wl.metadata)\\n obj := {\\\"volume\\\":volume,\\\"podname\\\": wlname}\\n\\n\\tmsga := {\\n\\t\\t\\\"alertMessage\\\": sprintf(\\\"%v: %v has {%v,%v} as hostPath volume\\n\\n\\n\\\", [wl.kind,wlname, volume]),\\n\\t\\t\\\"alert\\\": true,\\n\\t\\t\\\"prevent\\\": false,\\n\\t\\t\\\"alertScore\\\": 7,\\n\\t\\t\\\"alertObject\\\": [obj],\\n\\t\\n\\t}\\n}\\n\\n\\n\""
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
package resources
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
"github.com/open-policy-agent/opa/storage"
|
|
||||||
"github.com/open-policy-agent/opa/storage/inmem"
|
|
||||||
"github.com/open-policy-agent/opa/util"
|
|
||||||
"k8s.io/client-go/rest"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
RegoDependenciesPath = "/resources/rego/dependencies"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RegoDependenciesData struct {
|
|
||||||
K8sConfig RegoK8sConfig `json:"k8sconfig"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegoK8sConfig struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
IP string `json:"ip"`
|
|
||||||
Host string `json:"host"`
|
|
||||||
Port string `json:"port"`
|
|
||||||
CrtFile string `json:"crtfile"`
|
|
||||||
ClientCrtFile string `json:"clientcrtfile"`
|
|
||||||
ClientKeyFile string `json:"clientkeyfile"`
|
|
||||||
// ClientKeyFile string `json:"crtfile"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegoDependenciesDataMock() *RegoDependenciesData {
|
|
||||||
return NewRegoDependenciesData(k8sinterface.GetK8sConfig())
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegoDependenciesData(k8sConfig *rest.Config) *RegoDependenciesData {
|
|
||||||
|
|
||||||
regoDependenciesData := RegoDependenciesData{}
|
|
||||||
|
|
||||||
if k8sConfig != nil {
|
|
||||||
regoDependenciesData.K8sConfig = *NewRegoK8sConfig(k8sConfig)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ®oDependenciesData
|
|
||||||
}
|
|
||||||
func NewRegoK8sConfig(k8sConfig *rest.Config) *RegoK8sConfig {
|
|
||||||
|
|
||||||
host := k8sConfig.Host
|
|
||||||
if host == "" {
|
|
||||||
ip := os.Getenv("KUBERNETES_SERVICE_HOST")
|
|
||||||
port := os.Getenv("KUBERNETES_SERVICE_PORT")
|
|
||||||
host = fmt.Sprintf("https://%s:%s", ip, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
token := ""
|
|
||||||
if k8sConfig.BearerToken != "" {
|
|
||||||
token = fmt.Sprintf("Bearer %s", k8sConfig.BearerToken)
|
|
||||||
}
|
|
||||||
|
|
||||||
regoK8sConfig := RegoK8sConfig{
|
|
||||||
Token: token,
|
|
||||||
Host: host,
|
|
||||||
CrtFile: k8sConfig.CAFile,
|
|
||||||
ClientCrtFile: k8sConfig.CertFile,
|
|
||||||
ClientKeyFile: k8sConfig.KeyFile,
|
|
||||||
}
|
|
||||||
return ®oK8sConfig
|
|
||||||
}
|
|
||||||
func (data *RegoDependenciesData) TOStorage() (storage.Store, error) {
|
|
||||||
var jsonObj map[string]interface{}
|
|
||||||
bytesData, err := json.Marshal(*data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// glog.Infof("RegoDependenciesData: %s", bytesData)
|
|
||||||
if err := util.UnmarshalJSON(bytesData, &jsonObj); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return inmem.NewFromObject(jsonObj), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRegoDependenciesFromDir loads the policies list from *.rego file in given directory
|
|
||||||
func LoadRegoFiles(dir string) map[string]string {
|
|
||||||
|
|
||||||
modules := make(map[string]string)
|
|
||||||
|
|
||||||
// Compile the module. The keys are used as identifiers in error messages.
|
|
||||||
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err == nil && strings.HasSuffix(path, ".rego") && !info.IsDir() {
|
|
||||||
content, err := ioutil.ReadFile(path)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("LoadRegoFiles, Failed to load: %s: %v", path, err)
|
|
||||||
} else {
|
|
||||||
modules[strings.Trim(filepath.Base(path), ".rego")] = string(content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return modules
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadRegoModules loads the policies from variables
|
|
||||||
func LoadRegoModules() map[string]string {
|
|
||||||
|
|
||||||
modules := make(map[string]string)
|
|
||||||
modules["cautils"] = RegoCAUtils
|
|
||||||
modules["designators"] = RegoDesignators
|
|
||||||
modules["kubernetes.api.client"] = RegoKubernetesApiClient
|
|
||||||
|
|
||||||
return modules
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package resources
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestLoadRegoDependenciesFromDir(t *testing.T) {
|
|
||||||
dir, _ := os.Getwd()
|
|
||||||
t.Errorf("%s", filepath.Join(dir, "rego/dependencies"))
|
|
||||||
return
|
|
||||||
// modules := LoadRegoDependenciesFromDir("")
|
|
||||||
// if len(modules) == 0 {
|
|
||||||
// t.Errorf("modules len == 0")
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
const (
|
|
||||||
ComponentIdentifier = "Posture"
|
|
||||||
)
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package cautils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/armosec/kubescape/cautils/getter"
|
|
||||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ScanInfo struct {
|
|
||||||
Getters
|
|
||||||
PolicyIdentifier opapolicy.PolicyIdentifier
|
|
||||||
UseExceptions string
|
|
||||||
UseFrom string
|
|
||||||
UseDefault bool
|
|
||||||
Format string
|
|
||||||
Output string
|
|
||||||
ExcludedNamespaces string
|
|
||||||
InputPatterns []string
|
|
||||||
Silent bool
|
|
||||||
FailThreshold uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
type Getters struct {
|
|
||||||
ExceptionsGetter getter.IPolicyGetter
|
|
||||||
PolicyGetter getter.IPolicyGetter
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scanInfo *ScanInfo) Init() {
|
|
||||||
scanInfo.setUseFrom()
|
|
||||||
scanInfo.setUseExceptions()
|
|
||||||
scanInfo.setOutputFile()
|
|
||||||
scanInfo.setGetter()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scanInfo *ScanInfo) setUseExceptions() {
|
|
||||||
if scanInfo.UseExceptions != "" {
|
|
||||||
// load exceptions from file
|
|
||||||
scanInfo.ExceptionsGetter = getter.NewLoadPolicy(scanInfo.UseExceptions)
|
|
||||||
} else {
|
|
||||||
scanInfo.ExceptionsGetter = getter.NewArmoAPI()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func (scanInfo *ScanInfo) setUseFrom() {
|
|
||||||
if scanInfo.UseFrom != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if scanInfo.UseDefault {
|
|
||||||
scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func (scanInfo *ScanInfo) setGetter() {
|
|
||||||
if scanInfo.UseFrom != "" {
|
|
||||||
// load from file
|
|
||||||
scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom)
|
|
||||||
} else {
|
|
||||||
scanInfo.PolicyGetter = getter.NewDownloadReleasedPolicy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scanInfo *ScanInfo) setOutputFile() {
|
|
||||||
if scanInfo.Output == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if scanInfo.Format == "json" {
|
|
||||||
if filepath.Ext(scanInfo.Output) != "json" {
|
|
||||||
scanInfo.Output += ".json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if scanInfo.Format == "junit" {
|
|
||||||
if filepath.Ext(scanInfo.Output) != "xml" {
|
|
||||||
scanInfo.Output += ".xml"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scanInfo *ScanInfo) ScanRunningCluster() bool {
|
|
||||||
return len(scanInfo.InputPatterns) == 0
|
|
||||||
}
|
|
||||||
45
cmd/completion/completion.go
Normal file
45
cmd/completion/completion.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package completion
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var completionCmdExamples = `
|
||||||
|
|
||||||
|
# Enable BASH shell autocompletion
|
||||||
|
$ source <(kubescape completion bash)
|
||||||
|
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
|
||||||
|
|
||||||
|
# Enable ZSH shell autocompletion
|
||||||
|
$ source <(kubectl completion zsh)
|
||||||
|
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
func GetCompletionCmd() *cobra.Command {
|
||||||
|
completionCmd := &cobra.Command{
|
||||||
|
Use: "completion [bash|zsh|fish|powershell]",
|
||||||
|
Short: "Generate autocompletion script",
|
||||||
|
Long: "To load completions",
|
||||||
|
Example: completionCmdExamples,
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
|
||||||
|
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
switch strings.ToLower(args[0]) {
|
||||||
|
case "bash":
|
||||||
|
cmd.Root().GenBashCompletion(os.Stdout)
|
||||||
|
case "zsh":
|
||||||
|
cmd.Root().GenZshCompletion(os.Stdout)
|
||||||
|
case "fish":
|
||||||
|
cmd.Root().GenFishCompletion(os.Stdout, true)
|
||||||
|
case "powershell":
|
||||||
|
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return completionCmd
|
||||||
|
}
|
||||||
48
cmd/config/config.go
Normal file
48
cmd/config/config.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kubescape/kubescape/v2/core/meta"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
configExample = `
|
||||||
|
# View cached configurations
|
||||||
|
kubescape config view
|
||||||
|
|
||||||
|
# Delete cached configurations
|
||||||
|
kubescape config delete
|
||||||
|
|
||||||
|
# Set cached configurations
|
||||||
|
kubescape config set --help
|
||||||
|
`
|
||||||
|
setConfigExample = `
|
||||||
|
# Set account id
|
||||||
|
kubescape config set accountID <account id>
|
||||||
|
|
||||||
|
# Set client id
|
||||||
|
kubescape config set clientID <client id>
|
||||||
|
|
||||||
|
# Set access key
|
||||||
|
kubescape config set secretKey <access key>
|
||||||
|
|
||||||
|
# Set cloudAPIURL
|
||||||
|
kubescape config set cloudAPIURL <cloud API URL>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetConfigCmd(ks meta.IKubescape) *cobra.Command {
|
||||||
|
|
||||||
|
// configCmd represents the config command
|
||||||
|
configCmd := &cobra.Command{
|
||||||
|
Use: "config",
|
||||||
|
Short: "Handle cached configurations",
|
||||||
|
Example: configExample,
|
||||||
|
}
|
||||||
|
|
||||||
|
configCmd.AddCommand(getDeleteCmd(ks))
|
||||||
|
configCmd.AddCommand(getSetCmd(ks))
|
||||||
|
configCmd.AddCommand(getViewCmd(ks))
|
||||||
|
|
||||||
|
return configCmd
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user