forked from elastic/beats
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cloudid.go
128 lines (102 loc) · 3.53 KB
/
cloudid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// package cloudid contains functions for parsing the cloud.id and cloud.auth
// settings and modifying the configuration to take them into account.
package cloudid
import (
"encoding/base64"
"fmt"
"net/url"
"strings"
"github.com/pkg/errors"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
)
// OverwriteSettings modifies the received config object by overwriting the
// output.elasticsearch.hosts, output.elasticsearch.username, output.elasticsearch.password,
// setup.kibana.host settings based on values derived from the cloud.id and cloud.auth
// settings.
func OverwriteSettings(cfg *common.Config) error {
cloudID, _ := cfg.String("cloud.id", -1)
cloudAuth, _ := cfg.String("cloud.auth", -1)
if cloudID == "" && cloudAuth == "" {
// nothing to hack
return nil
}
logp.Debug("cloudid", "cloud.id: %s, cloud.auth: %s", cloudID, cloudAuth)
if cloudID == "" {
return errors.New("cloud.auth specified but cloud.id is empty. Please specify both.")
}
// cloudID overwrites
esURL, kibanaURL, err := decodeCloudID(cloudID)
if err != nil {
return errors.Errorf("Error decoding cloud.id: %v", err)
}
logp.Info("Setting Elasticsearch and Kibana URLs based on the cloud id: output.elasticsearch.hosts=%s and setup.kibana.host=%s", esURL, kibanaURL)
esURLConfig, err := common.NewConfigFrom([]string{esURL})
if err != nil {
return err
}
// Before enabling the ES output, check that no other output is enabled
tmp := struct {
Output common.ConfigNamespace `config:"output"`
}{}
if err := cfg.Unpack(&tmp); err != nil {
return err
}
if out := tmp.Output; out.IsSet() && out.Name() != "elasticsearch" {
return errors.Errorf("The cloud.id setting enables the Elasticsearch output, but you already have the %s output enabled in the config", out.Name())
}
err = cfg.SetChild("output.elasticsearch.hosts", -1, esURLConfig)
if err != nil {
return err
}
err = cfg.SetString("setup.kibana.host", -1, kibanaURL)
if err != nil {
return err
}
if cloudAuth != "" {
// cloudAuth overwrites
username, password, err := decodeCloudAuth(cloudAuth)
if err != nil {
return err
}
err = cfg.SetString("output.elasticsearch.username", -1, username)
if err != nil {
return err
}
err = cfg.SetString("output.elasticsearch.password", -1, password)
if err != nil {
return err
}
}
return nil
}
// decodeCloudID decodes the cloud.id into elasticsearch-URL and kibana-URL
func decodeCloudID(cloudID string) (string, string, error) {
// 1. Ignore anything before `:`.
idx := strings.LastIndex(cloudID, ":")
if idx >= 0 {
cloudID = cloudID[idx+1:]
}
// 2. base64 decode
decoded, err := base64.StdEncoding.DecodeString(cloudID)
if err != nil {
return "", "", errors.Wrapf(err, "base64 decoding failed on %s", cloudID)
}
// 3. separate based on `$`
words := strings.Split(string(decoded), "$")
if len(words) < 3 {
return "", "", errors.Errorf("Expected at least 3 parts in %s", string(decoded))
}
// 4. form the URLs
esURL := url.URL{Scheme: "https", Host: fmt.Sprintf("%s.%s:443", words[1], words[0])}
kibanaURL := url.URL{Scheme: "https", Host: fmt.Sprintf("%s.%s:443", words[2], words[0])}
return esURL.String(), kibanaURL.String(), nil
}
// decodeCloudAuth splits the cloud.auth into username and password.
func decodeCloudAuth(cloudAuth string) (string, string, error) {
idx := strings.Index(cloudAuth, ":")
if idx < 0 {
return "", "", errors.New("cloud.auth setting doesn't contain `:` to split between username and password")
}
return cloudAuth[0:idx], cloudAuth[idx+1:], nil
}