Skip to content

Commit ff1c788

Browse files
committed
Initial release
1 parent ee35688 commit ff1c788

File tree

9 files changed

+1943
-1
lines changed

9 files changed

+1943
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,5 @@
1212

1313
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
1414
.glide/
15+
16+
**.pem

README.md

Lines changed: 424 additions & 1 deletion
Large diffs are not rendered by default.

acm.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package certutils
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
"time"
8+
9+
"github.com/aws/aws-sdk-go/aws"
10+
"github.com/aws/aws-sdk-go/aws/session"
11+
"github.com/aws/aws-sdk-go/service/acm"
12+
"github.com/olekukonko/tablewriter"
13+
)
14+
15+
type ACM struct {
16+
client *acm.ACM
17+
}
18+
19+
type ACMDescription struct {
20+
arn string
21+
nameTag string
22+
status string
23+
inUseBy []string
24+
notAfter time.Time
25+
domainName string
26+
subjectAlternativeNames []string
27+
}
28+
29+
func NewACM(sess *session.Session) *ACM {
30+
return &ACM{
31+
client: acm.New(sess),
32+
}
33+
}
34+
35+
func createACMImportCertificateInput(cert, chain, pkey []byte) *acm.ImportCertificateInput {
36+
return &acm.ImportCertificateInput{
37+
Certificate: cert,
38+
CertificateChain: chain,
39+
PrivateKey: pkey,
40+
}
41+
}
42+
43+
func (a *ACM) Import(cert, chain, pkey []byte) (string, string, error) {
44+
out, err := a.client.ImportCertificate(createACMImportCertificateInput(cert, chain, pkey))
45+
46+
return *out.CertificateArn, fmt.Sprintf("Imported %s", *out.CertificateArn), err
47+
}
48+
49+
func createACMListCertificatesInput(statuses []string, maxItems int64, nextToken string) *acm.ListCertificatesInput {
50+
linput := &acm.ListCertificatesInput{}
51+
52+
if len(statuses) > 0 {
53+
linput.SetCertificateStatuses(aws.StringSlice(statuses))
54+
}
55+
56+
if maxItems > 0 {
57+
linput.SetMaxItems(maxItems)
58+
}
59+
60+
if nextToken != "" {
61+
linput.SetNextToken(nextToken)
62+
}
63+
64+
return linput
65+
}
66+
67+
func (a *ACM) listTags(arn string) (*acm.ListTagsForCertificateOutput, error) {
68+
input := &acm.ListTagsForCertificateInput{}
69+
input.SetCertificateArn(arn)
70+
71+
return a.client.ListTagsForCertificate(input)
72+
}
73+
74+
func (a *ACM) getNameTag(arn string) (string, error) {
75+
out, err := a.listTags(arn)
76+
if err != nil {
77+
return "", err
78+
}
79+
80+
for _, tag := range out.Tags {
81+
if strings.ToLower(aws.StringValue(tag.Key)) == "name" {
82+
return *tag.Value, nil
83+
}
84+
}
85+
86+
return "", fmt.Errorf("Name tag not found")
87+
}
88+
89+
func (a *ACM) List(statuses string, maxItems int64, nextToken string) ([]ACMDescription, error) {
90+
cout, err := a.client.ListCertificates(createACMListCertificatesInput(SplitStatuses(statuses), maxItems, nextToken))
91+
92+
descs := make([]ACMDescription, 0, len(cout.CertificateSummaryList))
93+
for _, summary := range cout.CertificateSummaryList {
94+
dcout, err := a.client.DescribeCertificate(&acm.DescribeCertificateInput{
95+
CertificateArn: summary.CertificateArn,
96+
})
97+
if err != nil {
98+
return []ACMDescription{}, err
99+
}
100+
101+
cert := dcout.Certificate
102+
nameTag, _ := a.getNameTag(*cert.CertificateArn)
103+
104+
desc := ACMDescription{
105+
arn: *cert.CertificateArn,
106+
nameTag: nameTag,
107+
status: *cert.Status,
108+
inUseBy: aws.StringValueSlice(cert.InUseBy),
109+
notAfter: aws.TimeValue(cert.NotAfter),
110+
domainName: *cert.DomainName,
111+
subjectAlternativeNames: aws.StringValueSlice(cert.SubjectAlternativeNames),
112+
}
113+
114+
descs = append(descs, desc)
115+
}
116+
117+
return descs, err
118+
}
119+
120+
func (a *ACM) ListArns(statuses string, maxItems int64, nextToken string) ([]string, error) {
121+
descs, err := a.List(statuses, maxItems, nextToken)
122+
123+
arns := make([]string, 0, len(descs))
124+
for _, desc := range descs {
125+
arns = append(arns, desc.arn)
126+
}
127+
128+
return arns, err
129+
}
130+
131+
func toACMTags(tags []Tag) []*acm.Tag {
132+
if len(tags) <= 0 {
133+
return []*acm.Tag{}
134+
}
135+
136+
acmTags := make([]*acm.Tag, 0, len(tags))
137+
138+
for _, t := range tags {
139+
acmTag := &acm.Tag{
140+
Key: aws.String(t.Key),
141+
Value: aws.String(t.Value),
142+
}
143+
144+
acmTags = append(acmTags, acmTag)
145+
}
146+
147+
return acmTags
148+
}
149+
150+
func createACMAddTagsToCertificateInput(arn string, tags []Tag) *acm.AddTagsToCertificateInput {
151+
tinput := &acm.AddTagsToCertificateInput{}
152+
tinput.SetCertificateArn(arn)
153+
tinput.SetTags(toACMTags(tags))
154+
155+
return tinput
156+
}
157+
158+
func (a *ACM) AddTags(arn string, tags []Tag) error {
159+
_, err := a.client.AddTagsToCertificate(createACMAddTagsToCertificateInput(arn, tags))
160+
161+
return err
162+
}
163+
164+
func createACMDeleteCertificateInput(arn string) *acm.DeleteCertificateInput {
165+
dinput := &acm.DeleteCertificateInput{}
166+
167+
dinput.SetCertificateArn(arn)
168+
169+
return dinput
170+
}
171+
172+
func (a *ACM) Delete(arn string) (string, error) {
173+
_, err := a.client.DeleteCertificate(createACMDeleteCertificateInput(arn))
174+
175+
return fmt.Sprintf("Deleted %s", arn), err
176+
}
177+
178+
func (a *ACM) ReadableList(descs []ACMDescription) {
179+
table := tablewriter.NewWriter(os.Stdout)
180+
181+
table.SetHeader([]string{"Name tag", "Domain Name", "Additional Name", "In Use?", "Not After", "Certificate Arn"})
182+
table.SetAutoMergeCells(true)
183+
table.SetRowLine(true)
184+
185+
for _, desc := range descs {
186+
inUse := "No"
187+
if len(desc.inUseBy) > 0 {
188+
inUse = "Yes"
189+
}
190+
for _, name := range desc.subjectAlternativeNames {
191+
if name == desc.domainName {
192+
continue
193+
}
194+
table.Append([]string{desc.nameTag, desc.domainName, name, inUse, desc.notAfter.String(), desc.arn})
195+
}
196+
}
197+
198+
table.Render()
199+
}

alb.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package certutils
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"github.com/aws/aws-sdk-go/aws"
8+
"github.com/aws/aws-sdk-go/aws/session"
9+
"github.com/aws/aws-sdk-go/service/elbv2"
10+
"github.com/olekukonko/tablewriter"
11+
)
12+
13+
type ALB struct {
14+
client *elbv2.ELBV2
15+
}
16+
17+
type ALBDescription struct {
18+
name string
19+
dnsname string
20+
certs []ALBCertificate
21+
}
22+
23+
type ALBCertificate struct {
24+
arn string
25+
port int64
26+
listenerArn string
27+
}
28+
29+
func NewALB(sess *session.Session) *ALB {
30+
return &ALB{
31+
client: elbv2.New(sess),
32+
}
33+
}
34+
35+
func createALBDescribeListenersInput(lbArn string) *elbv2.DescribeListenersInput {
36+
input := &elbv2.DescribeListenersInput{}
37+
38+
input.SetLoadBalancerArn(lbArn)
39+
40+
return input
41+
}
42+
43+
func (alb *ALB) getLBs(certFilter string) ([]ALBDescription, error) {
44+
input := &elbv2.DescribeLoadBalancersInput{}
45+
out, err := alb.client.DescribeLoadBalancers(input)
46+
lbs := make([]ALBDescription, 0, len(out.LoadBalancers))
47+
for _, lb := range out.LoadBalancers {
48+
albdesc := ALBDescription{}
49+
50+
albdesc.dnsname = *lb.DNSName
51+
albdesc.name = *lb.LoadBalancerName
52+
53+
lout, err := alb.client.DescribeListeners(createALBDescribeListenersInput(*lb.LoadBalancerArn))
54+
if err != nil {
55+
return []ALBDescription{}, err
56+
}
57+
58+
for _, l := range lout.Listeners {
59+
for _, cert := range l.Certificates {
60+
if certFilter != "" && certFilter != *cert.CertificateArn {
61+
continue
62+
}
63+
64+
albcert := ALBCertificate{
65+
arn: *cert.CertificateArn,
66+
port: *l.Port,
67+
listenerArn: *l.ListenerArn,
68+
}
69+
albdesc.certs = append(albdesc.certs, albcert)
70+
}
71+
}
72+
73+
if len(albdesc.certs) < 1 {
74+
continue
75+
}
76+
77+
lbs = append(lbs, albdesc)
78+
}
79+
return lbs, err
80+
}
81+
82+
func (alb *ALB) List(certFilter string) ([]ALBDescription, error) {
83+
return alb.getLBs(certFilter)
84+
}
85+
86+
func (alb *ALB) getListener(name string) (*elbv2.Listener, error) {
87+
lbinput := &elbv2.DescribeLoadBalancersInput{}
88+
names := []string{name}
89+
lbinput.SetNames(aws.StringSlice(names))
90+
lbout, err := alb.client.DescribeLoadBalancers(lbinput)
91+
if err != nil {
92+
return &elbv2.Listener{}, err
93+
}
94+
95+
if len(lbout.LoadBalancers) < 1 {
96+
return &elbv2.Listener{}, fmt.Errorf("Listener not found")
97+
}
98+
99+
linput := &elbv2.DescribeListenersInput{}
100+
linput.SetLoadBalancerArn(*lbout.LoadBalancers[0].LoadBalancerArn)
101+
102+
lout, err := alb.client.DescribeListeners(linput)
103+
if err != nil {
104+
return &elbv2.Listener{}, err
105+
}
106+
107+
if len(lout.Listeners) < 1 {
108+
return &elbv2.Listener{}, fmt.Errorf("Listener not found")
109+
}
110+
111+
return lout.Listeners[0], nil
112+
}
113+
114+
func createALBModifyListenerInput(listenerArn, certArn string) *elbv2.ModifyListenerInput {
115+
input := &elbv2.ModifyListenerInput{}
116+
117+
cert := &elbv2.Certificate{}
118+
cert.SetCertificateArn(certArn)
119+
120+
certs := []*elbv2.Certificate{cert}
121+
122+
input.SetCertificates(certs)
123+
124+
input.SetListenerArn(listenerArn)
125+
126+
return input
127+
}
128+
129+
func (alb *ALB) Update(name string, certArn string) error {
130+
l, err := alb.getListener(name)
131+
if err != nil {
132+
return err
133+
}
134+
135+
_, err = alb.client.ModifyListener(createALBModifyListenerInput(*l.ListenerArn, certArn))
136+
137+
return err
138+
}
139+
140+
func albUpdateMsg(name string, port int64, src, dest string) string {
141+
return fmt.Sprintf("Updated %s:%d %s -> %s", name, port, src, dest)
142+
}
143+
144+
func (alb *ALB) BulkUpdate(srcCertArn, destCertArn string, dryRun bool) ([]string, error) {
145+
lbs, err := alb.getLBs(srcCertArn)
146+
if err != nil {
147+
return []string{}, err
148+
}
149+
150+
updates := make([]string, 0)
151+
if dryRun {
152+
updates = append(updates, dryRunMsg()...)
153+
}
154+
155+
for _, lb := range lbs {
156+
for _, cert := range lb.certs {
157+
if dryRun {
158+
updates = append(updates, albUpdateMsg(lb.name, cert.port, srcCertArn, destCertArn))
159+
} else {
160+
_, err := alb.client.ModifyListener(createALBModifyListenerInput(cert.listenerArn, destCertArn))
161+
162+
if err != nil {
163+
return []string{}, err
164+
}
165+
updates = append(updates, albUpdateMsg(lb.name, cert.port, srcCertArn, destCertArn))
166+
}
167+
}
168+
}
169+
170+
return updates, nil
171+
}
172+
173+
func (alb *ALB) ReadableList(descs []ALBDescription) {
174+
table := tablewriter.NewWriter(os.Stdout)
175+
176+
table.SetHeader([]string{"Name", "Port", "Listener SSL Certificate"})
177+
table.SetAutoMergeCells(true)
178+
table.SetRowLine(true)
179+
180+
for _, desc := range descs {
181+
for _, cert := range desc.certs {
182+
table.Append([]string{desc.name, fmt.Sprint(cert.port), cert.arn})
183+
}
184+
}
185+
186+
table.Render()
187+
}

0 commit comments

Comments
 (0)