Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitleaksignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/github/workspace/api/v1/loggingservice_types.go:generic-api-key:694
/github/workspace/api/v1/loggingservice_types.go:generic-api-key:697
/github/workspace/api/v1/loggingservice_types.go:generic-api-key:695
/github/workspace/api/v1/loggingservice_types.go:generic-api-key:698
1 change: 1 addition & 0 deletions api/v1/loggingservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ type Graylog struct {
InitSetupImage string `json:"initSetupImage"`
User string `json:"-"`
Password string `json:"-"`
ElasticsearchHost string `json:"-"`
NodeSelectorKey string `json:"nodeSelectorKey,omitempty"`
InitContainerDockerImage string `json:"initContainerDockerImage,omitempty"`
GraylogSecretName string `json:"graylogSecretName"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ data:
elasticsearchHost: {{ .elasticsearchHost | default "" | b64enc }}
password: {{ .password | default "" | b64enc }}
user: {{ .user | default "admin" | b64enc }}
rootPasswordSha2: {{ .password | default "" | sha256sum | b64enc }}
{{- if .s3Archive }}
awsAccessKey: {{ .awsAccessKey | default ""| b64enc }}
awsSecretKey: {{ .awsSecretKey | default "" | b64enc }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,6 @@ spec:
value: {{ .Values.podMonitor.scrapeTimeout | default "10s" }}
- name: LOG_LEVEL
value: {{ .Values.logLevel | default "info" }}
{{- if .Values.graylog.install }}
- name: GRAYLOG_USERNAME
valueFrom:
secretKeyRef:
key: user
name: {{ default "graylog-secret" .Values.graylog.graylogSecretName }}
- name: GRAYLOG_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: {{ default "graylog-secret" .Values.graylog.graylogSecretName }}
- name: ELASTICSEARCH_HOST
valueFrom:
secretKeyRef:
key: elasticsearchHost
name: {{ default "graylog-secret" .Values.graylog.graylogSecretName }}
{{- end }}
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
Expand Down
35 changes: 20 additions & 15 deletions controllers/graylog/assets/statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ spec:
- key: node-id
path: node-id
defaultMode: 0666
- name: graylog-credentials-files
secret:
secretName: {{ .Values.Graylog.GraylogSecretName }}
defaultMode: 0440
items:
- key: elasticsearchHost
path: elasticsearchHost
- key: user
path: user
- key: rootPasswordSha2
path: rootPasswordSha2
{{ if .Values.Graylog.AuthProxy }}
{{ if .Values.Graylog.AuthProxy.Install }}
- name: graylog-auth-proxy-config
Expand Down Expand Up @@ -294,21 +305,12 @@ spec:
{{ if and .Values.Graylog.TLS .Values.Graylog.TLS.HTTP }}
-Djavax.net.ssl.trustStore=/usr/share/graylog/data/ssl/cacerts.jks
{{ end }}
- name: GRAYLOG_ELASTICSEARCH_HOSTS
valueFrom:
secretKeyRef:
key: elasticsearchHost
name: {{ .Values.Graylog.GraylogSecretName }}
- name: GRAYLOG_USERNAME
valueFrom:
secretKeyRef:
key: user
name: {{ .Values.Graylog.GraylogSecretName }}
- name: GRAYLOG_PASSWORD
valueFrom:
secretKeyRef:
key: password
name: {{ .Values.Graylog.GraylogSecretName }}
- name: GRAYLOG_ELASTICSEARCH_HOSTS__FILE
value: /usr/share/graylog/data/k8s-graylog-secret/elasticsearchHost
- name: GRAYLOG_ROOT_USERNAME__FILE
value: /usr/share/graylog/data/k8s-graylog-secret/user
- name: GRAYLOG_ROOT_PASSWORD_SHA2__FILE
value: /usr/share/graylog/data/k8s-graylog-secret/rootPasswordSha2
- name: GRAYLOG_SNAPSHOT_DIRECTORY
value: {{ .Values.Graylog.PathRepo }}
{{- if .Values.Graylog.S3Archive }}
Expand Down Expand Up @@ -345,6 +347,9 @@ spec:
successThreshold: 1
timeoutSeconds: 5
volumeMounts:
- name: graylog-credentials-files
mountPath: /usr/share/graylog/data/k8s-graylog-secret
readOnly: true
- name: data
mountPath: /usr/share/graylog/data
readOnly: false
Expand Down
15 changes: 15 additions & 0 deletions controllers/graylog/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
r.Log.Info("Start Graylog reconciliation")

if cr.Spec.Graylog != nil && cr.Spec.Graylog.IsInstall() {
if err := r.setCredentials(cr); err != nil {
return err
}
connector, err := utils.CreateConnector(ctx, cr, configs, clientSet)
if err != nil {
return err
Expand Down Expand Up @@ -200,7 +203,7 @@
if secret.Data != nil && secret.Data["user"] != nil && len(secret.Data["user"]) > 0 {
usr = string(secret.Data["user"])
} else {
err := errors.New("can not find user for Graylog in the secret " + cr.Spec.Graylog.GraylogSecretName + " in the namespace " + cr.GetNamespace())

Check failure on line 206 in controllers/graylog/reconciler.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal " in the namespace " 3 times.

See more on https://sonarcloud.io/project/issues?id=Netcracker_qubership-logging-operator&issues=AZ3eBUd1IACWm1DO74jU&open=AZ3eBUd1IACWm1DO74jU&pullRequest=283
return err
}
cr.Spec.Graylog.User = usr
Expand All @@ -213,6 +216,18 @@
return err
}
cr.Spec.Graylog.Password = pwd

if secret.Data != nil && len(secret.Data["elasticsearchHost"]) > 0 {
cr.Spec.Graylog.ElasticsearchHost = string(secret.Data["elasticsearchHost"])
} else {
return errors.New("can not find elasticsearchHost for Graylog in the secret " + cr.Spec.Graylog.GraylogSecretName + " in the namespace " + cr.GetNamespace())
}

if utils.EnsureSecretRootPasswordSHA2(secret, pwd) {
if err := r.Client.Update(context.TODO(), secret); err != nil {
return err
}
}
return nil
}

Expand Down
28 changes: 12 additions & 16 deletions controllers/graylog/utils/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import (
"context"
"crypto/tls"
"embed"
"fmt"
"net/http"
"net/url"
"os"
"strings"
"time"

Expand Down Expand Up @@ -78,20 +78,12 @@ func CreateConnector(ctx context.Context, cr *loggingService.LoggingService, ass
Timeout: time.Duration(util.ConnectionTimeout) * time.Second,
}

if len(cr.Spec.Graylog.User) != 0 && len(cr.Spec.Graylog.Password) != 0 {
user = &util.Creds{
Name: cr.Spec.Graylog.User,
Password: cr.Spec.Graylog.Password,
}
} else {
name := os.Getenv("GRAYLOG_USERNAME")
pwd := os.Getenv("GRAYLOG_PASSWORD")
if len(name) != 0 && len(pwd) != 0 {
user = &util.Creds{
Name: name,
Password: pwd,
}
}
if len(cr.Spec.Graylog.User) == 0 || len(cr.Spec.Graylog.Password) == 0 {
return nil, fmt.Errorf("graylog API credentials are empty; expected user and password from Secret %q (reconcile loads them into spec)", cr.Spec.Graylog.GraylogSecretName)
}
user = &util.Creds{
Name: cr.Spec.Graylog.User,
Password: cr.Spec.Graylog.Password,
}

restClient := &util.RestClient{
Expand Down Expand Up @@ -124,8 +116,12 @@ func CreateConnector(ctx context.Context, cr *loggingService.LoggingService, ass
}

if len(host) == 0 {
esHost := cr.Spec.Graylog.ElasticsearchHost
if esHost == "" {
return nil, fmt.Errorf("OpenSearch/Elasticsearch host is empty; expected elasticsearchHost in Secret %q (reconcile loads it into spec) or spec.graylog.openSearch", cr.Spec.Graylog.GraylogSecretName)
}
var u *url.URL
u, err = url.Parse(os.Getenv("ELASTICSEARCH_HOST"))
u, err = url.Parse(esHost)
if err != nil {
return nil, err
}
Expand Down
30 changes: 30 additions & 0 deletions controllers/graylog/utils/root_password_sha2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package utils

import (
"crypto/sha256"
"encoding/hex"
"strings"

util "github.com/Netcracker/qubership-logging-operator/controllers/utils"
corev1 "k8s.io/api/core/v1"
)

// EnsureSecretRootPasswordSHA2 sets secret.Data[util.GraylogSecretKeyRootPasswordSHA2] to SHA256hex(plainPassword)
// when missing or different. Returns true if the secret was modified (caller must persist with Update).
func EnsureSecretRootPasswordSHA2(secret *corev1.Secret, plainPassword string) bool {
sum := sha256.Sum256([]byte(plainPassword))
want := hex.EncodeToString(sum[:])
key := util.GraylogSecretKeyRootPasswordSHA2
got := ""
if secret.Data != nil && secret.Data[key] != nil {
got = strings.TrimSpace(string(secret.Data[key]))
}
if got == want {
return false
}
if secret.Data == nil {
secret.Data = map[string][]byte{}
}
secret.Data[key] = []byte(want)
return true
}
89 changes: 51 additions & 38 deletions controllers/graylog/utils/watch-secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,50 +148,63 @@ func (w *SecretEventWatcher) updatePassword(secret *corev1.Secret, cr *loggingSe
}
if cr.Spec.Graylog.Password == pwd {
w.Log.Info("Password did not change")
return nil
} else {
return w.syncRootPasswordSHA2SecretKey(cr, pwd)
}

cr.Spec.Graylog.Password = pwd
cr.Spec.Graylog.Password = pwd

cm, err := w.Clientset.CoreV1().ConfigMaps(cr.GetNamespace()).Get(context.TODO(), util.GraylogComponentName, metav1.GetOptions{})
cm, err := w.Clientset.CoreV1().ConfigMaps(cr.GetNamespace()).Get(context.TODO(), util.GraylogComponentName, metav1.GetOptions{})
if err != nil {
return err
}
config := cm.Data[util.GraylogConfigFileName]
if config == "" {
w.Log.Info("Config of Graylog is empty in configmap", "configmap", util.GraylogComponentName, "namespace", cr.GetNamespace())
return w.syncRootPasswordSHA2SecretKey(cr, pwd)
}
var lines []string
scanner := bufio.NewScanner(strings.NewReader(config))
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err = scanner.Err(); err != nil {
return err
}
var result strings.Builder
var changed bool
for i := range lines {
if strings.HasPrefix(lines[i], util.GraylogPasswordField) {
h := sha256.New()
h.Write([]byte(pwd))
bs := h.Sum(nil)
lines[i] = fmt.Sprintf("%s = %s", util.GraylogPasswordField, hex.EncodeToString(bs))
changed = true
}
result.WriteString(lines[i])
result.WriteString("\r\n")
}
if changed {
cm.Data[util.GraylogConfigFileName] = result.String()
updatedCm, err := w.Clientset.CoreV1().ConfigMaps(cr.GetNamespace()).Update(context.TODO(), cm, metav1.UpdateOptions{})
if err != nil {
w.Log.Error(err, "Update of config map failed", "configmap", updatedCm.GetName(), "namespace", updatedCm.GetNamespace())
return err
}
config := cm.Data[util.GraylogConfigFileName]
if config == "" {
w.Log.Info("Config of Graylog is empty in configmap", "configmap", util.GraylogComponentName, "namespace", cr.GetNamespace())
return nil
}
var lines []string
scanner := bufio.NewScanner(strings.NewReader(config))
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err = scanner.Err(); err != nil {
return err
}
var result strings.Builder
var changed bool
for i := range lines {
if strings.HasPrefix(lines[i], util.GraylogPasswordField) {
h := sha256.New()
h.Write([]byte(pwd))
bs := h.Sum(nil)
lines[i] = fmt.Sprintf("%s = %s", util.GraylogPasswordField, hex.EncodeToString(bs))
changed = true
}
result.WriteString(lines[i])
result.WriteString("\r\n")
}
if changed {
cm.Data[util.GraylogConfigFileName] = result.String()
updatedCm, err := w.Clientset.CoreV1().ConfigMaps(cr.GetNamespace()).Update(context.TODO(), cm, metav1.UpdateOptions{})
if err != nil {
w.Log.Error(err, "Update of config map failed", "configmap", updatedCm.GetName(), "namespace", updatedCm.GetNamespace())
return err
}
}
}
return w.syncRootPasswordSHA2SecretKey(cr, pwd)
}

func (w *SecretEventWatcher) syncRootPasswordSHA2SecretKey(cr *loggingService.LoggingService, plainPassword string) error {
sec, err := w.Clientset.CoreV1().Secrets(cr.GetNamespace()).Get(context.TODO(), cr.Spec.Graylog.GraylogSecretName, metav1.GetOptions{})
if err != nil {
return err
}
if !EnsureSecretRootPasswordSHA2(sec, plainPassword) {
return nil
}
if _, err := w.Clientset.CoreV1().Secrets(cr.GetNamespace()).Update(context.TODO(), sec, metav1.UpdateOptions{}); err != nil {
w.Log.Error(err, "Update of graylog secret failed", "secret", sec.GetName(), "namespace", sec.GetNamespace())
return err
}
return nil
}
1 change: 1 addition & 0 deletions controllers/utils/vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ var (
GraylogMongoUpgradeJobTimeout = time.Minute * 2
GraylogLabels = map[string]string{"name": "graylog"}
GraylogSecretSelector = "graylog=secret"
GraylogSecretKeyRootPasswordSHA2 = "rootPasswordSha2"
GraylogConfigFileName = "graylog.conf"
GraylogPasswordField = "root_password_sha2"
GraylogUserField = "root_username"
Expand Down
Loading