Skip to content

LOG-6863: safety parsing/read fields to avoid messing data in log event #3031

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

Closed
wants to merge 2 commits into from
Closed
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
5 changes: 4 additions & 1 deletion internal/generator/vector/output/common/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ func TemplateRemap(componentID string, inputs []string, userTemplate, field, des

// TransformUserTemplateToVRL converts the user entered template to VRL compatible syntax
// Example: foo-{.log_type||"none"} -> "foo-" + to_string!(.log_type||"none")
func TransformUserTemplateToVRL(userTemplate string) string {
func TransformUserTemplateToVRL(userTemplate string, suffix ...string) string {
// Finds and replaces expressions defined in `{}` with to_string!()
replacedUserTemplate := ReplaceBracketWithToString(userTemplate, "to_string!(%s)")
if len(suffix) > 0 && suffix[0] != "" {
replacedUserTemplate = ReplaceBracketWithToString(userTemplate, "to_string!("+suffix[0]+"%s)")
}

// Finding all matches of to_string!() returning their start + end indices
matchedIndices := splitRegex.FindAllStringSubmatchIndex(replacedUserTemplate, -1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@
type = "remap"
inputs = ["application"]
source = '''
. = merge(., parse_json!(string!(.message))) ?? .
if .log_type == "infrastructure" && .log_source == "node" {
._internal.syslog.tag = to_string!(.systemd.u.SYSLOG_IDENTIFIER || "")
._internal.syslog.proc_id = to_string!(.systemd.t.PID || "")
}
if .log_source == "container" {
._internal.syslog.tag = join!([.kubernetes.namespace_name, .kubernetes.pod_name, .kubernetes.container_name], "")
._internal.syslog.severity = .level
._internal.syslog.facility = "user"
#Remove non-alphanumeric characters
#Remove non-alphanumeric characters
._internal.syslog.tag = replace(._internal.syslog.tag, r'[^a-zA-Z0-9]', "")
#Truncate the sanitized tag to 32 characters
._internal.syslog.tag = truncate(._internal.syslog.tag, 32)
._internal.syslog.severity = .level
._internal.syslog.facility = "user"
}
if .log_type == "audit" {
._internal.syslog.tag = .log_source
._internal.syslog.severity = "informational"
._internal.syslog.facility = "security"
}

.facility = to_string!(._internal.syslog.facility || "user")
.severity = to_string!(._internal.syslog.severity || "informational")
.proc_id = to_string!(._internal.syslog.proc_id || "-")
Expand Down
264 changes: 168 additions & 96 deletions internal/generator/vector/output/syslog/syslog.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,42 @@ import (
)

const (
TCP = `tcp`
TLS = `tls`
TCP = `tcp`
TLS = `tls`
ParsedMsg = "parsed_msg"
defFacility = `{._internal.syslog.facility || "user"}`
defSeverity = `{._internal.syslog.severity || "informational"}`
defProcId = `{._internal.syslog.proc_id || "-"}`
defTag = `{._internal.syslog.tag || ""}`
defAppName = `{._internal.syslog.app_name || "-"}`
defMsgId = `{._internal.syslog.msg_id || "-"}`

nodeTag = `._internal.syslog.tag = to_string!(.systemd.u.SYSLOG_IDENTIFIER || "")`
nodeProcId = `._internal.syslog.proc_id = to_string!(.systemd.t.PID || "")`

containerTag = `._internal.syslog.tag = join!([.kubernetes.namespace_name, .kubernetes.pod_name, .kubernetes.container_name], "")
#Remove non-alphanumeric characters
._internal.syslog.tag = replace(._internal.syslog.tag, r'[^a-zA-Z0-9]', "")
#Truncate the sanitized tag to 32 characters
._internal.syslog.tag = truncate(._internal.syslog.tag, 32)
`
containerSeverity = `._internal.syslog.severity = .level`
containerFacility = `._internal.syslog.facility = "user"`
auditTag = `._internal.syslog.tag = .log_source`
auditSeverity = `._internal.syslog.severity = "informational"`
auditFacility = `._internal.syslog.facility = "security"`
msgId = `._internal.syslog.msg_id = .log_source`
nodeAppName = `._internal.syslog.app_name = to_string!(.systemd.u.SYSLOG_IDENTIFIER||"-")`
nodeProcId2 = `._internal.syslog.proc_id = to_string!(.systemd.t.PID||"-")`
containerAppName = `._internal.syslog.app_name = join!([.kubernetes.namespace_name, .kubernetes.pod_name, .kubernetes.container_name], "_")`
containerProcId = `._internal.syslog.proc_id = to_string!(.kubernetes.pod_id||"-")`
containerSeverity2 = `._internal.syslog.severity = .level`
containerFacility2 = `._internal.syslog.facility = "user"`
auditAppName = `._internal.syslog.app_name = .log_source`
auditProcId2 = `._internal.syslog.proc_id = to_string!(.auditID || "-")`

auditSeverity2 = `._internal.syslog.severity = "informational"`
auditFacility2 = `._internal.syslog.facility = "security"`
)

type Syslog struct {
Expand Down Expand Up @@ -61,64 +95,34 @@ type SyslogEncodingRemap struct {
EncodingFields EncodingTemplateField
PayloadKey string
RFC string
Defaults string
ParseMsg bool
}

func (ser SyslogEncodingRemap) Name() string {
return "syslogEncodingRemap"
}

func (ser SyslogEncodingRemap) Template() string {
return `{{define "` + ser.Name() + `" -}}
return fmt.Sprintf(`{{define "`+ser.Name()+`" -}}
[transforms.{{.ComponentID}}]
type = "remap"
inputs = {{.Inputs}}
source = '''
. = merge(., parse_json!(string!(.message))) ?? .

{{if eq .RFC "RFC3164" -}}
if .log_type == "infrastructure" && .log_source == "node" {
._internal.syslog.tag = to_string!(.systemd.u.SYSLOG_IDENTIFIER || "")
._internal.syslog.proc_id = to_string!(.systemd.t.PID || "")
}
if .log_source == "container" {
._internal.syslog.tag = join!([.kubernetes.namespace_name, .kubernetes.pod_name, .kubernetes.container_name], "")
._internal.syslog.severity = .level
._internal.syslog.facility = "user"
#Remove non-alphanumeric characters
._internal.syslog.tag = replace(._internal.syslog.tag, r'[^a-zA-Z0-9]', "")
#Truncate the sanitized tag to 32 characters
._internal.syslog.tag = truncate(._internal.syslog.tag, 32)

}
if .log_type == "audit" {
._internal.syslog.tag = .log_source
._internal.syslog.severity = "informational"
._internal.syslog.facility = "security"
}
{{if .Defaults}}
{{.Defaults}}
{{end}}

{{if eq .RFC "RFC5424" -}}
._internal.syslog.msg_id = .log_source

if .log_type == "infrastructure" && .log_source == "node" {
._internal.syslog.app_name = to_string!(.systemd.u.SYSLOG_IDENTIFIER||"-")
._internal.syslog.proc_id = to_string!(.systemd.t.PID||"-")
}
if .log_source == "container" {
._internal.syslog.app_name = join!([.kubernetes.namespace_name, .kubernetes.pod_name, .kubernetes.container_name], "_")
._internal.syslog.proc_id = to_string!(.kubernetes.pod_id||"-")
._internal.syslog.severity = .level
._internal.syslog.facility = "user"
}
if .log_type == "audit" {
._internal.syslog.app_name = .log_source
._internal.syslog.proc_id = to_string!(.auditID || "-")
._internal.syslog.severity = "informational"
._internal.syslog.facility = "security"
{{if .EncodingFields.FieldVRLList -}}
{{if .ParseMsg}}
_tmp, err = parse_json(string!(.message))
if err != null {
_tmp = .
log(err, level: "error")
} else {
_tmp = merge!(.,_tmp)
}
%s = _tmp
{{end}}

{{if .EncodingFields.FieldVRLList -}}
{{range $templatePair := .EncodingFields.FieldVRLList -}}
.{{$templatePair.Field}} = {{$templatePair.VRLString}}
{{end -}}
Expand All @@ -139,7 +143,7 @@ if is_null({{.PayloadKey}}) {
{{end}}
'''
{{end -}}
`
`, ParsedMsg)
}

type SyslogEncoding struct {
Expand Down Expand Up @@ -177,15 +181,15 @@ func New(id string, o obs.OutputSpec, inputs []string, secrets observability.Sec
}
}
parseEncodingID := vectorhelpers.MakeID(id, "parse_encoding")
templateFieldPairs := getEncodingTemplatesAndFields(*o.Syslog)
templateFieldPairs, needToParseMsg := getEncodingTemplatesAndFields(*o.Syslog)
u, _ := url.Parse(o.Syslog.URL)
sink := Output(id, o, []string{parseEncodingID}, secrets, op, u.Scheme, u.Host)
if strategy != nil {
strategy.VisitSink(sink)
}

syslogElements := []Element{
parseEncoding(parseEncodingID, inputs, templateFieldPairs, o.Syslog),
parseEncoding(parseEncodingID, inputs, templateFieldPairs, needToParseMsg, o.Syslog),
sink,
}

Expand Down Expand Up @@ -214,63 +218,42 @@ func Output(id string, o obs.OutputSpec, inputs []string, secrets observability.

// getEncodingTemplatesAndFields determines which encoding fields are templated
// so that the templates can be parsed to appropriate VRL
func getEncodingTemplatesAndFields(s obs.Syslog) EncodingTemplateField {
func getEncodingTemplatesAndFields(s obs.Syslog) (EncodingTemplateField, bool) {
templateFields := EncodingTemplateField{
FieldVRLList: []FieldVRLStringPair{},
}

if s.Facility == "" {
s.Facility = `{._internal.syslog.facility || "user"}`
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "facility",
VRLString: commontemplate.TransformUserTemplateToVRL(s.Facility),
})

if s.Severity == "" {
s.Severity = `{._internal.syslog.severity || "informational"}`
var needToParseMsg = false

appendField := func(fieldName string, value string, defaultVal string) {
if value == "" {
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: fieldName,
VRLString: commontemplate.TransformUserTemplateToVRL(defaultVal),
})
} else {
if commontemplate.PathRegex.MatchString(value) {
needToParseMsg = true
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: fieldName,
VRLString: commontemplate.TransformUserTemplateToVRL(value, ParsedMsg),
})
}
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "severity",
VRLString: commontemplate.TransformUserTemplateToVRL(s.Severity),
})

if s.ProcId == "" {
s.ProcId = `{._internal.syslog.proc_id || "-"}`
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "proc_id",
VRLString: commontemplate.TransformUserTemplateToVRL(s.ProcId),
})
appendField("facility", s.Facility, defFacility)
appendField("severity", s.Severity, defSeverity)
appendField("proc_id", s.ProcId, defProcId)

if s.RFC == obs.SyslogRFC3164 {
if s.AppName == "" {
s.AppName = `{._internal.syslog.tag || ""}`
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "tag",
VRLString: commontemplate.TransformUserTemplateToVRL(s.AppName),
})

appendField("tag", s.AppName, defTag)
} else {
if s.AppName == "" {
s.AppName = `{._internal.syslog.app_name || "-"}`
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "app_name",
VRLString: commontemplate.TransformUserTemplateToVRL(s.AppName),
})

if s.MsgId == "" {
s.MsgId = `{._internal.syslog.msg_id || "-"}`
}
templateFields.FieldVRLList = append(templateFields.FieldVRLList, FieldVRLStringPair{
Field: "msg_id",
VRLString: commontemplate.TransformUserTemplateToVRL(s.MsgId),
})
appendField("app_name", s.AppName, defAppName)
appendField("msg_id", s.MsgId, defMsgId)
}

return templateFields
return templateFields, needToParseMsg
}

func Encoding(id string, o obs.OutputSpec, templatePairs []FieldVRLStringPair) []Element {
Expand All @@ -297,13 +280,15 @@ func Encoding(id string, o obs.OutputSpec, templatePairs []FieldVRLStringPair) [
return encodingFields
}

func parseEncoding(id string, inputs []string, templatePairs EncodingTemplateField, o *obs.Syslog) Element {
func parseEncoding(id string, inputs []string, templatePairs EncodingTemplateField, needToParseMsg bool, o *obs.Syslog) Element {
return SyslogEncodingRemap{
ComponentID: id,
Inputs: vectorhelpers.MakeInputs(inputs...),
EncodingFields: templatePairs,
PayloadKey: PayloadKey(o.PayloadKey),
RFC: string(o.RFC),
Defaults: buildDefaults(o),
ParseMsg: needToParseMsg,
}
}

Expand Down Expand Up @@ -345,3 +330,90 @@ var keyre = regexp.MustCompile(`^\$(\.[[:word:]]*)+$`)
func IsKeyExpr(str string) bool {
return keyre.MatchString(str)
}

func buildDefaults(o *obs.Syslog) string {
var builder strings.Builder
type defaultRule struct {
cond string
appName string
procId string
severity string
facility string
}
var defaultRules []defaultRule

if o.RFC == obs.SyslogRFC3164 {
if o.ProcId == "" || o.AppName == "" {
defaultRules = append(defaultRules, defaultRule{
cond: `.log_type == "infrastructure" && .log_source == "node"`,
appName: ifEmpty(o.AppName, nodeTag),
procId: ifEmpty(o.ProcId, nodeProcId),
})
}
if o.AppName == "" || o.Severity == "" || o.Facility == "" {
defaultRules = append(defaultRules, defaultRule{
cond: `.log_source == "container"`,
appName: ifEmpty(o.AppName, containerTag),
severity: ifEmpty(o.Severity, containerSeverity),
facility: ifEmpty(o.Facility, containerFacility),
}, defaultRule{
cond: `.log_type == "audit"`,
appName: ifEmpty(o.AppName, auditTag),
severity: ifEmpty(o.Severity, auditSeverity),
facility: ifEmpty(o.Facility, auditFacility),
})
}
}

if o.RFC == obs.SyslogRFC5424 {
builder.WriteString(msgId + "\n")
if o.ProcId == "" || o.AppName == "" {
defaultRules = append(defaultRules, defaultRule{
cond: `.log_type == "infrastructure" && .log_source == "node"`,
appName: ifEmpty(o.AppName, nodeAppName),
procId: ifEmpty(o.ProcId, nodeProcId2),
})
}
if o.AppName == "" || o.ProcId == "" || o.Severity == "" || o.Facility == "" {
defaultRules = append(defaultRules, defaultRule{
cond: `.log_source == "container"`,
appName: ifEmpty(o.AppName, containerAppName),
procId: ifEmpty(o.ProcId, containerProcId),
severity: ifEmpty(o.Severity, containerSeverity),
facility: ifEmpty(o.Facility, containerFacility),
}, defaultRule{
cond: `.log_type == "audit"`,
appName: ifEmpty(o.AppName, auditAppName),
procId: ifEmpty(o.ProcId, auditProcId2),
severity: ifEmpty(o.Severity, auditSeverity2),
facility: ifEmpty(o.Facility, auditFacility2),
})
}
}

for _, b := range defaultRules {
builder.WriteString(fmt.Sprintf("if %s {\n", b.cond))
if b.appName != "" {
builder.WriteString(b.appName + "\n")
}
if b.procId != "" {
builder.WriteString(b.procId + "\n")
}
if b.severity != "" {
builder.WriteString(b.severity + "\n")
}
if b.facility != "" {
builder.WriteString(b.facility + "\n")
}
builder.WriteString("}\n")
}

return builder.String()
}

func ifEmpty(val, defaultVal string) string {
if val == "" {
return defaultVal
}
return ""
}
Loading