-
Notifications
You must be signed in to change notification settings - Fork 718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ensure Kibana encryption key is specified #2278
Changes from 17 commits
f5a8cf7
9382da0
cfcbb3a
7939dc1
0eb2530
c17fefa
b0c6b56
fc9b85d
971c1b4
a60dc2e
d316577
a71842a
36731a2
d9ed1e2
0ba45f1
d333cfd
3c123c1
417cac3
767047a
0b9fc95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,11 +15,19 @@ import ( | |
"github.com/elastic/cloud-on-k8s/pkg/controller/common/settings" | ||
"github.com/elastic/cloud-on-k8s/pkg/controller/kibana/es" | ||
"github.com/elastic/cloud-on-k8s/pkg/utils/k8s" | ||
"github.com/elastic/go-ucfg" | ||
corev1 "k8s.io/api/core/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/types" | ||
"k8s.io/apimachinery/pkg/util/rand" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
) | ||
|
||
// Kibana configuration settings file | ||
// SettingsFilename is the name of the Kibana configuration settings file | ||
const SettingsFilename = "kibana.yml" | ||
|
||
var log = logf.Log.WithName("kibana-config") | ||
|
||
// CanonicalConfig contains configuration for Kibana ("kibana.yml"), | ||
// as a hierarchical key-value configuration. | ||
type CanonicalConfig struct { | ||
|
@@ -28,6 +36,8 @@ type CanonicalConfig struct { | |
|
||
// NewConfigSettings returns the Kibana configuration settings for the given Kibana resource. | ||
func NewConfigSettings(client k8s.Client, kb kbv1.Kibana, versionSpecificCfg *settings.CanonicalConfig) (CanonicalConfig, error) { | ||
currentConfig := getExistingConfig(client, kb) | ||
filteredCurrCfg := filterExistingConfig(currentConfig) | ||
specConfig := kb.Spec.Config | ||
if specConfig == nil { | ||
specConfig = &commonv1.Config{} | ||
|
@@ -42,7 +52,12 @@ func NewConfigSettings(client k8s.Client, kb kbv1.Kibana, versionSpecificCfg *se | |
kibanaTLSCfg := settings.MustCanonicalConfig(kibanaTLSSettings(kb)) | ||
|
||
if !kb.RequiresAssociation() { | ||
if err := cfg.MergeWith(versionSpecificCfg, kibanaTLSCfg, userSettings); err != nil { | ||
// merge the configuration with userSettings last so they take precedence | ||
if err := cfg.MergeWith( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can the following happen?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but that seems okay? Deleting the secret will re-generate the secret with a new encryption key, so there at least is a way to do that if that is the goal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What I meant was this also apply to any other settings. apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-sample
spec:
version: 7.5.0
count: 1
elasticsearchRef:
name: "elasticsearch-sample"
config:
logging.verbose: true # temporarily enable verbose logging Then 1 hour later once I'm done with looking at Kibana logs I remove my extra config and reapply the Kibana manifest without verbose logging enabled: apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
name: kibana-sample
spec:
version: 7.5.0
count: 1
elasticsearchRef:
name: "elasticsearch-sample" I think the operator code would still merge the new empty user-provided config with the current config which does have Please correct me if I'm wrong, I may be misreading the way we do the config merge. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like we have two setting types: for some we want to always flow from the resource (like Should we just get the specific settings that we know should be persisted from the secret? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah that's a good point and a big miss on my part Seb, good catch.
I think this makes sense. |
||
filteredCurrCfg, | ||
versionSpecificCfg, | ||
kibanaTLSCfg, | ||
userSettings); err != nil { | ||
return CanonicalConfig{}, err | ||
} | ||
return CanonicalConfig{cfg}, nil | ||
|
@@ -55,6 +70,7 @@ func NewConfigSettings(client k8s.Client, kb kbv1.Kibana, versionSpecificCfg *se | |
|
||
// merge the configuration with userSettings last so they take precedence | ||
err = cfg.MergeWith( | ||
filteredCurrCfg, | ||
versionSpecificCfg, | ||
kibanaTLSCfg, | ||
settings.MustCanonicalConfig(elasticsearchTLSSettings(kb)), | ||
|
@@ -73,12 +89,57 @@ func NewConfigSettings(client k8s.Client, kb kbv1.Kibana, versionSpecificCfg *se | |
return CanonicalConfig{cfg}, nil | ||
} | ||
|
||
// getExistingConfig retrieves the canonical config for a given Kibana, if one exists | ||
func getExistingConfig(client k8s.Client, kb kbv1.Kibana) *settings.CanonicalConfig { | ||
var secret corev1.Secret | ||
err := client.Get(types.NamespacedName{Name: SecretName(kb), Namespace: kb.Namespace}, &secret) | ||
if err != nil && apierrors.IsNotFound(err) { | ||
log.V(1).Info("Kibana config secret does not exist", "kibana_namespace", kb.Namespace, "kibana_name", kb.Name) | ||
anyasabo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return nil | ||
} else if err != nil { | ||
log.Error(err, "Error retrieving kibana config secret", "kibana_namespace", kb.Namespace, "kibana_name", kb.Name) | ||
return nil | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would expect we return the error here. Otherwise it means a failing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm that makes sense. I'll change it to return an error here and exit the reconcile loop |
||
} | ||
rawCfg, exists := secret.Data[SettingsFilename] | ||
if !exists { | ||
log.Error(nil, "No kibana config file in secret", "secret_namespace", secret.Namespace, "secret_name", secret.Name, "key", SettingsFilename) | ||
return nil | ||
} | ||
cfg, err := settings.ParseConfig(rawCfg) | ||
if err != nil { | ||
log.Error(err, "Error parsing existing kibana config in secret", "secret_namespace", secret.Namespace, "secret_name", secret.Name, "key", SettingsFilename) | ||
return nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we end up erroring out above, we might also error out on those 2. |
||
return cfg | ||
} | ||
|
||
// filterExistingConfig filters an existing config for only items we want to preserve between spec changes | ||
// because they cannot be generated deterministically, e.g. encryption keys | ||
func filterExistingConfig(cfg *settings.CanonicalConfig) *settings.CanonicalConfig { | ||
if cfg == nil { | ||
return nil | ||
} | ||
val, err := (*ucfg.Config)(cfg).String(XpackSecurityEncryptionKey, -1, settings.Options...) | ||
if err != nil { | ||
log.V(1).Info("Current config does not contain key", "key", XpackSecurityEncryptionKey, "error", err) | ||
return nil | ||
} | ||
filteredCfg, err := settings.NewSingleValue(XpackSecurityEncryptionKey, val) | ||
if err != nil { | ||
log.Error(err, "Error filtering current config") | ||
return nil | ||
} | ||
return filteredCfg | ||
} | ||
|
||
func baseSettings(kb kbv1.Kibana) map[string]interface{} { | ||
return map[string]interface{}{ | ||
ServerName: kb.Name, | ||
ServerHost: "0", | ||
ElasticSearchHosts: []string{kb.AssociationConf().GetURL()}, | ||
XpackMonitoringUiContainerElasticsearchEnabled: true, | ||
// this will get overriden if one already exists or is specified by the user | ||
XpackSecurityEncryptionKey: rand.String(64), | ||
anyasabo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like
MustNewSingleValue
can now just be a call toNewSingleValue
+ panic on error?