Skip to content

Commit 18dba61

Browse files
authored
feat: add strong/weak key report (#14)
1 parent edbb58d commit 18dba61

File tree

2 files changed

+155
-82
lines changed

2 files changed

+155
-82
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ docker pull docker.pkg.github.com/jef/audit-org-keys/audit-org-keys:<tag>
1919
docker run --rm -it \
2020
--env "GITHUB_ORGANIZATION=$GITHUB_ORGANIZATION" \
2121
--env "GITHUB_PAT=$GITHUB_PAT" \
22-
audit-org-keys:<tag>
22+
"docker.pkg.github.com/jef/audit-org-keys/audit-org-keys:<tag>"
2323
```
2424

2525
> :point_right: View [Available arguments](#available-arguments) and [Available environment variables](#available-environment-variables) below if you'd like to customize input and output

main.go

Lines changed: 154 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io/ioutil"
99
"net/http"
1010
"os"
11+
"regexp"
1112
"strings"
1213
"sync"
1314
"sync/atomic"
@@ -18,30 +19,108 @@ type member struct {
1819
Keys []string
1920
}
2021

21-
func main() {
22-
ms := getMembers()
23-
ms = getKeys(ms)
24-
printReport(ms)
22+
type keyTable struct {
23+
keyDsaSize uint32
24+
keyEcdsaSize uint32
25+
keyEd25519Size uint32
26+
keyRsaSize uint32
27+
keyStrongRsaSize uint32
28+
keyWeakRsaSize uint32
29+
userDsaSize uint32
30+
userEcdsaSize uint32
31+
userEd25519Size uint32
32+
userRsaSize uint32
33+
userWithKeySize uint32
34+
userWithoutKeySize uint32
35+
userWithMultipleKeySize uint32
36+
keySize uint32
37+
strongKeySize uint32
38+
weakKeySize uint32
39+
userSize uint32
40+
userWithStrongKeySize uint32
41+
userWithWeakKeySize uint32
42+
userWithWeakKey []member
43+
}
44+
45+
func printReport(kt keyTable) {
46+
withKey := [][]string{
47+
{"users with keys",
48+
"DSA",
49+
fmt.Sprintf("%d (%.2f%%)", kt.keyDsaSize, float32(kt.keyDsaSize)/float32(kt.keySize)*100),
50+
fmt.Sprintf("%d (%.2f%%)", kt.userDsaSize, float32(kt.userDsaSize)/float32(kt.userSize)*100)},
51+
{"",
52+
"ECDSA",
53+
fmt.Sprintf("%d (%.2f%%)", kt.keyEcdsaSize, float32(kt.keyEcdsaSize)/float32(kt.keySize)*100),
54+
fmt.Sprintf("%d (%.2f%%)", kt.userEcdsaSize, float32(kt.userEcdsaSize)/float32(kt.userSize)*100)},
55+
{"",
56+
"Ed25519",
57+
fmt.Sprintf("%d (%.2f%%)", kt.keyEd25519Size, float32(kt.keyEd25519Size)/float32(kt.keySize)*100),
58+
fmt.Sprintf("%d (%.2f%%)", kt.userEd25519Size, float32(kt.userEd25519Size)/float32(kt.userSize)*100)},
59+
{"",
60+
"RSA",
61+
fmt.Sprintf("%d (%.2f%%)", kt.keyRsaSize, float32(kt.keyRsaSize)/float32(kt.keySize)*100),
62+
fmt.Sprintf("%d (%.2f%%)", kt.userRsaSize, float32(kt.userRsaSize)/float32(kt.userSize)*100)},
63+
}
64+
65+
withoutKey := [][]string{
66+
{"users without keys",
67+
"",
68+
"",
69+
fmt.Sprintf("%d (%.2f%%)", kt.userWithoutKeySize, float32(kt.userWithoutKeySize)/float32(kt.userSize)*100)},
70+
}
71+
72+
withMultipleKey := [][]string{{"users with multiple keys",
73+
"",
74+
"",
75+
fmt.Sprintf("%d (%.2f%%)", kt.userWithMultipleKeySize, float32(kt.userWithMultipleKeySize)/float32(kt.userSize)*100)},
76+
}
77+
78+
strongKey := [][]string{
79+
{"users with strong keys",
80+
"",
81+
fmt.Sprintf("%d (%.2f%%)", kt.strongKeySize, float32(kt.strongKeySize)/float32(kt.keySize)*100),
82+
fmt.Sprintf("%d (%.2f%%)", kt.userWithStrongKeySize, float32(kt.userWithStrongKeySize)/float32(kt.userWithKeySize)*100)},
83+
}
84+
85+
weakKey := [][]string{
86+
{"users with weak keys",
87+
"",
88+
fmt.Sprintf("%d (%.2f%%)", kt.weakKeySize, float32(kt.weakKeySize)/float32(kt.keySize)*100),
89+
fmt.Sprintf("%d (%.2f%%)", kt.userWithWeakKeySize, float32(kt.userWithWeakKeySize)/float32(kt.userWithKeySize)*100)},
90+
}
91+
92+
t := tablewriter.NewWriter(os.Stdout)
93+
t.SetHeader([]string{"description", "key type", "# of keys", "# of users"})
94+
t.SetHeaderColor(tablewriter.Colors{tablewriter.FgCyanColor},
95+
tablewriter.Colors{tablewriter.FgCyanColor},
96+
tablewriter.Colors{tablewriter.FgCyanColor},
97+
tablewriter.Colors{tablewriter.FgCyanColor})
98+
t.SetFooter([]string{"", "total", fmt.Sprintf("%d", kt.keySize), fmt.Sprintf("%d", kt.userSize)})
99+
t.SetFooterColor(tablewriter.Colors{tablewriter.FgCyanColor},
100+
tablewriter.Colors{tablewriter.FgCyanColor},
101+
tablewriter.Colors{tablewriter.FgCyanColor},
102+
tablewriter.Colors{tablewriter.FgCyanColor})
103+
t.SetRowLine(true)
104+
t.AppendBulk(withKey)
105+
t.AppendBulk(withoutKey)
106+
t.AppendBulk(withMultipleKey)
107+
t.AppendBulk(strongKey)
108+
t.AppendBulk(weakKey)
109+
t.Render()
110+
111+
if len(kt.userWithWeakKey) > 0 {
112+
zap.S().Info("users with weak keys:")
113+
for _, m := range kt.userWithWeakKey {
114+
zap.S().Infof("%s", m.Login)
115+
}
116+
}
25117
}
26118

27-
func printReport(ms []member) {
119+
func generateKeyTable(ms []member) keyTable {
28120
var wg sync.WaitGroup
29121

30-
var (
31-
keyDsaSize uint32
32-
keyEddsaSize uint32
33-
keyEd25519Size uint32
34-
keyRsaSize uint32
35-
userDsaSize uint32
36-
userEddsaSize uint32
37-
userEd25519Size uint32
38-
userRsaSize uint32
39-
userWithKeySize uint32
40-
userWithoutKeySize uint32
41-
userWithMultipleKeySize uint32
42-
totalKeySize uint32
43-
totalUserSize = len(ms)
44-
)
122+
var kt keyTable
123+
kt.userSize = uint32(len(ms))
45124

46125
for _, m := range ms {
47126
wg.Add(1)
@@ -51,44 +130,68 @@ func printReport(ms []member) {
51130
defer wg.Done()
52131

53132
var (
54-
hasDsa bool
55-
hasRsa bool
56-
hasEddsa bool
57-
hasEd25519 bool
133+
hasDsa bool
134+
hasEcdsa bool
135+
hasEd25519 bool
136+
hasRsa bool
137+
userHasStrongRsa bool
58138
)
59139

60140
for _, key := range m.Keys {
61-
atomic.AddUint32(&totalKeySize, 1)
141+
atomic.AddUint32(&kt.keySize, 1)
62142

63143
switch {
64144
case strings.Contains(key, "ssh-dsa"):
65-
atomic.AddUint32(&keyDsaSize, 1)
66145
hasDsa = true
67-
case strings.Contains(key, "ssh-rsa"):
68-
atomic.AddUint32(&keyRsaSize, 1)
69-
hasRsa = true
146+
atomic.AddUint32(&kt.keyDsaSize, 1)
147+
atomic.AddUint32(&kt.weakKeySize, 1)
70148
case strings.Contains(key, "ssh-eddsa"):
71-
atomic.AddUint32(&keyEddsaSize, 1)
72-
hasEddsa = true
149+
hasEcdsa = true
150+
atomic.AddUint32(&kt.keyEcdsaSize, 1)
151+
atomic.AddUint32(&kt.weakKeySize, 1)
73152
case strings.Contains(key, "ssh-ed25519"):
74-
atomic.AddUint32(&keyEd25519Size, 1)
75153
hasEd25519 = true
154+
atomic.AddUint32(&kt.keyEd25519Size, 1)
155+
atomic.AddUint32(&kt.strongKeySize, 1)
156+
case strings.Contains(key, "ssh-rsa"):
157+
hasRsa = true
158+
atomic.AddUint32(&kt.keyRsaSize, 1)
159+
if isRsaStrong(key) {
160+
userHasStrongRsa = true
161+
atomic.AddUint32(&kt.keyStrongRsaSize, 1)
162+
atomic.AddUint32(&kt.strongKeySize, 1)
163+
} else {
164+
userHasStrongRsa = false
165+
atomic.AddUint32(&kt.keyWeakRsaSize, 1)
166+
atomic.AddUint32(&kt.weakKeySize, 1)
167+
}
76168
}
77169
}
78170

79171
switch {
80172
case hasDsa:
81-
atomic.AddUint32(&userDsaSize, 1)
82-
case hasRsa:
83-
atomic.AddUint32(&userRsaSize, 1)
84-
case hasEddsa:
85-
atomic.AddUint32(&userEddsaSize, 1)
173+
atomic.AddUint32(&kt.userDsaSize, 1)
174+
atomic.AddUint32(&kt.userWithWeakKeySize, 1)
175+
kt.userWithWeakKey = append(kt.userWithWeakKey, m)
176+
case hasEcdsa:
177+
atomic.AddUint32(&kt.userEcdsaSize, 1)
178+
atomic.AddUint32(&kt.userWithWeakKeySize, 1)
179+
kt.userWithWeakKey = append(kt.userWithWeakKey, m)
86180
case hasEd25519:
87-
atomic.AddUint32(&userEd25519Size, 1)
181+
atomic.AddUint32(&kt.userEd25519Size, 1)
182+
atomic.AddUint32(&kt.userWithStrongKeySize, 1)
183+
case hasRsa:
184+
atomic.AddUint32(&kt.userRsaSize, 1)
185+
if userHasStrongRsa {
186+
atomic.AddUint32(&kt.userWithStrongKeySize, 1)
187+
} else {
188+
atomic.AddUint32(&kt.userWithWeakKeySize, 1)
189+
kt.userWithWeakKey = append(kt.userWithWeakKey, m)
190+
}
88191
}
89192

90193
if len(m.Keys) == 0 {
91-
atomic.AddUint32(&userWithoutKeySize, 1)
194+
atomic.AddUint32(&kt.userWithoutKeySize, 1)
92195
if *showUsers == "without" || *showUsers == "all" {
93196
zap.S().Infow("retrieved keys",
94197
"user", m.Login,
@@ -98,7 +201,7 @@ func printReport(ms []member) {
98201
}
99202

100203
if len(m.Keys) > 0 {
101-
atomic.AddUint32(&userWithKeySize, 1)
204+
atomic.AddUint32(&kt.userWithKeySize, 1)
102205
if *showUsers == "with" || *showUsers == "all" {
103206
zap.S().Infow("retrieved keys",
104207
"user", m.Login,
@@ -108,7 +211,7 @@ func printReport(ms []member) {
108211
}
109212

110213
if len(m.Keys) > 1 {
111-
atomic.AddUint32(&userWithMultipleKeySize, 1)
214+
atomic.AddUint32(&kt.userWithMultipleKeySize, 1)
112215
if *showUsers == "multiple" || *showUsers == "all" {
113216
zap.S().Infow("retrieved keys",
114217
"user", m.Login,
@@ -120,47 +223,13 @@ func printReport(ms []member) {
120223
}
121224
wg.Wait()
122225

123-
withKey := [][]string{
124-
{"users with keys", "DSA",
125-
fmt.Sprintf("%d (%.2f%%)", keyDsaSize, float32(keyDsaSize)/float32(totalKeySize)*100),
126-
fmt.Sprintf("%d (%.2f%%)", userDsaSize, float32(userDsaSize)/float32(totalUserSize)*100)},
127-
{"", "RSA",
128-
fmt.Sprintf("%d (%.2f%%)", keyRsaSize, float32(keyRsaSize)/float32(totalKeySize)*100),
129-
fmt.Sprintf("%d (%.2f%%)", userRsaSize, float32(userRsaSize)/float32(totalUserSize)*100)},
130-
{"", "ECDSA",
131-
fmt.Sprintf("%d (%.2f%%)", keyEddsaSize, float32(keyEddsaSize)/float32(totalKeySize)*100),
132-
fmt.Sprintf("%d (%.2f%%)", userEddsaSize, float32(userEddsaSize)/float32(totalUserSize)*100)},
133-
{"", "Ed25519",
134-
fmt.Sprintf("%d (%.2f%%)", keyEd25519Size, float32(keyEd25519Size)/float32(totalKeySize)*100),
135-
fmt.Sprintf("%d (%.2f%%)", userEd25519Size, float32(userEd25519Size)/float32(totalUserSize)*100)},
136-
}
137-
138-
withoutKey := [][]string{
139-
{"users without keys", "", "", fmt.Sprintf("%d (%.2f%%)", userWithoutKeySize, float32(userWithoutKeySize)/float32(totalUserSize)*100)},
140-
}
141-
142-
withMultipleKey := [][]string{
143-
{"users with multiple keys", "", "", fmt.Sprintf("%d (%.2f%%)", userWithMultipleKeySize, float32(userWithMultipleKeySize)/float32(totalUserSize)*100)},
144-
}
226+
return kt
227+
}
145228

146-
t := tablewriter.NewWriter(os.Stdout)
147-
t.SetHeader([]string{"description", "key type", "# of keys", "# of users"})
148-
t.SetHeaderColor(tablewriter.Colors{tablewriter.FgCyanColor},
149-
tablewriter.Colors{tablewriter.FgCyanColor},
150-
tablewriter.Colors{tablewriter.FgCyanColor},
151-
tablewriter.Colors{tablewriter.FgCyanColor},
152-
)
153-
t.SetFooter([]string{"", "total", fmt.Sprintf("%d", totalKeySize), fmt.Sprintf("%d", totalUserSize)})
154-
t.SetFooterColor(tablewriter.Colors{tablewriter.FgCyanColor},
155-
tablewriter.Colors{tablewriter.FgCyanColor},
156-
tablewriter.Colors{tablewriter.FgCyanColor},
157-
tablewriter.Colors{tablewriter.FgCyanColor},
158-
)
159-
t.SetRowLine(true)
160-
t.AppendBulk(withKey)
161-
t.AppendBulk(withoutKey)
162-
t.AppendBulk(withMultipleKey)
163-
t.Render()
229+
func isRsaStrong(key string) bool {
230+
r := regexp.MustCompile(`(ssh-rsa) (.*)`)
231+
keyArray := r.FindStringSubmatch(key)
232+
return len(keyArray[2]) >= 372
164233
}
165234

166235
func getKeys(ms []member) []member {
@@ -250,3 +319,7 @@ func getMembers() []member {
250319

251320
return members
252321
}
322+
323+
func main() {
324+
printReport(generateKeyTable(getKeys(getMembers())))
325+
}

0 commit comments

Comments
 (0)