Add Native Syslog support to CloudWatch Agent#2125
Conversation
|
This PR was marked stale due to lack of activity. |
|
This PR was marked stale due to lack of activity. |
|
This PR was marked stale due to lack of activity. |
|
This PR was marked stale due to lack of activity. |
| func buildAttributeCondition(attr, value string) string { | ||
| escaped := escapeOTTL(value) | ||
| if isGlobPattern(value) { | ||
| return fmt.Sprintf(`IsMatch(attributes["%s"], "%s")`, attr, escaped) |
There was a problem hiding this comment.
We detect glob syntax (*, ?, [) here but then emit the value straight into IsMatch(attributes["hostname"], "web-*"). OTTL's IsMatch compiles that as an RE2 regular expression, not a shell glob — so web-* matches "we" followed by zero-or-more "b"s, and something like host.domain-* treats the . as "any character." Customers writing what looks like a glob will get surprising routing.
Two ways to go: translate the glob into an anchored regex (web-* → ^web-.*$, escaping the non-glob metacharacters), or drop the glob detection and document these fields as regex. Either is fine — just want the behavior to match what a user would expect from the config.
| } | ||
|
|
||
| func escapeOTTL(s string) string { | ||
| s = strings.ReplaceAll(s, `\`, `\\`) |
There was a problem hiding this comment.
escapeOTTL escapes backslashes and double-quotes, which stops a value from breaking out of the string literal — but any other regex metacharacters in a hostname, app-name, or filter expression still flow through into IsMatch. Since these values end up in a regex, I'd escape the full metacharacter set (or regexp.QuoteMeta the user input before interpolating) so a stray character can't silently change what a rule matches. This overlaps with the glob comment above — handling it once at the point we build the condition string can cover both.
| } | ||
| } | ||
|
|
||
| return fmt.Sprintf("%s://%s:%s", protocol, host, port), nil |
There was a problem hiding this comment.
splitHostPort strips the brackets off an IPv6 host, but this reassembly puts it back as proto://host:port without them — so tcp://[::1]:514 comes out as tcp://::1:514, which is ambiguous/invalid for net.Listen. net.JoinHostPort(host, port) on the way out handles the bracketing correctly. Worth updating the translator_test.go case too — it currently asserts the unbracketed form, so it's locking in the behavior.
| seen := make(map[string]int) // address → section index | ||
| for i, section := range sections { | ||
| for _, listener := range normalizeListeners(section) { | ||
| addr, _ := listener["listen_address"].(string) |
There was a problem hiding this comment.
This uniqueness check compares the listen_address strings before resolveListenAddress applies defaulting, so two entries that resolve to the same bind slip past it — e.g. tcp:// vs tcp://127.0.0.1:5514, or tcp://0.0.0.0:514 vs tcp://:514. They'd only collide later at bind time. Resolving each address first and keying seen on the canonical form would catch these at config time with the nice error message you already have here.
| "type": "object", | ||
| "description": "Configuration for a syslog listener", | ||
| "properties": { | ||
| "listen_address": { |
There was a problem hiding this comment.
syslogListenerDefinition has no required array, so a {"listeners": [{}]} entry passes schema validation and then fails later at translate time on the empty address. Adding "required": ["listen_address"] here (or applying the same defaulting the top-level shorthand uses) would surface it up front with a clearer message.
| // This avoids relying on confighttp.ClientConfig.Headers which uses | ||
| // configopaque.String and gets nil'd during YAML serialization. | ||
| if rt.ext.cfg.LogGroup != "" { | ||
| req.Header.Set("x-aws-log-group", rt.ext.cfg.LogGroup) |
There was a problem hiding this comment.
RoundTrip sets headers on the incoming *http.Request. The http.RoundTripper contract says the transport shouldn't modify the request, and this can race under retries / HTTP-2 where the same request may be in flight more than once. Cloning first (req = req.Clone(req.Context())) and setting headers on the copy keeps it safe. I know this path is inactive today, but it'll bite once OTLP mode is on.
|
|
||
| func TestStart_StoresHost(t *testing.T) { | ||
| authID := component.MustNewID("sigv4auth") | ||
| cfg := &Config{AdditionalAuth: &authID} |
There was a problem hiding this comment.
This Config has no Region or LogsProvisionTimeout, but Start() calls cfg.Validate() first, which returns "region is required" — so require.NoError on the Start call below would fail as written. Same setup in TestRoundTripper_MissingAdditionalAuth. Setting Region + a positive LogsProvisionTimeout on the config should fix both. Separately, metadata.yaml doesn't define a tests.config, which the generated lifecycle test needs to build a valid config.
Description of the issue
The current CloudWatch Agent has no support for native syslog support. This means customers who have Syslog sources and using the CWA must use some other agent, some other bridging application, or write syslog to disk and have the CWA read those files. This adds complexity to customer configurations that want to use CWA or alternatively customers will use other agents.
Description of changes
This change adds native Syslog support to the CloudWatch Agent. Customers are able to define multiple Syslog listeners using tcp/udp and add various filters and routing rules to ensure log entries land on the correct log groups. These changes support both Syslog format RFCs, 5424 as well as legacy RFC 3164. Additionally, support for server and client side TLS support is included as part of the listener.
The changes are broken down in the following areas:
section level.
IsMatch(attributes["appname"], "...")).
collisions.
Example Config:
License
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
Tests
translator/translate/otel/pipeline/syslogtranslator_test.go
TestNewTranslatorsNilConfTestNewTranslatorsNoSyslogKeyTestNewTranslatorsSingleListenerNoRulesTestNewTranslatorsWithRoutingRulesTestInputPipelineTranslateTestOutputPipelineTranslateTestDeriveReceiverNameTestBuildOTTLConditionTestIsGlobPatternTestToFiltersTestNewTranslatorsWithTLSTestFilterProcessorTranslator_IDTestFilterProcessorTranslator_TranslateTestRoutingConnectorTranslator_IDTestRoutingConnectorTranslator_TranslateTestSigV4AuthTranslator_IDTestSigV4AuthTranslator_TranslateTestSigV4AuthTranslator_NoRoleARNTestBuildFilterConditionsTestNewTranslatorsMultiSectionTestNewTranslatorsArrayFormSingleSectionTestNewTranslatorsDuplicateListenerAcrossSectionsTestNewTranslatorsUniqueListenersAcrossSectionsTestProvisionerTranslator_IDTestProvisionerTranslator_TranslateTestProvisionerTranslator_ZeroRetentionexporter_test.go
TestCWLExporterTranslator_IDTestCWLExporterTranslator_TranslateTestOTLPExporterTranslator_IDTestOTLPExporterTranslator_TranslateTestOTLPExporterTranslator_EndpointOverrideTestNewExporterTranslator_Dispatchtranslator/translate/otel/receiver/syslogTestTranslatorIDTestTranslateTCPTestTranslateUDPTestTranslateInvalidAddressTestTranslateUnsupportedProtocolTestTranslateRFC3164TestTranslateDefaultProtocolTestTranslateTCPWithTLSTestTranslateTCPWithClientCAFileTestTranslateUDPIgnoresTLSTestParseListenAddresstranslator/config— Schema ValidationTestSyslogSchema_SingleListenerTestSyslogSchema_MultipleListenersTestSyslogSchema_WithRoutingTestSyslogSchema_WithFiltersTestSyslogSchema_WithTLSTestSyslogSchema_WithProtocolTestSyslogSchema_WithRetentionTestSyslogSchema_FullConfigTestSyslogSchema_InvalidTopLevelKeyTestSyslogSchema_MissingListenAddressTestSyslogSchema_MissingLogGroupNameTestSyslogSchema_InvalidProtocolTestSyslogSchema_InvalidTLSMinVersionTestSyslogSchema_InvalidRetentionTestSyslogSchema_InvalidFacilityTestSyslogSchema_RoutingMissingMatchTestSyslogSchema_RoutingMissingLogGroupTestSyslogSchema_RoutingEmptyMatchTestSyslogSchema_UnknownListenerFieldTestSyslogSchema_UnknownTLSFieldTestSyslogSchema_WithClientCAFileTestSyslogSchema_UnknownMatchFieldTestSyslogSchema_ArrayForm_SingleSectionTestSyslogSchema_ArrayForm_MultipleSectionsTestSyslogSchema_ArrayForm_EmptyArrayTestSyslogSchema_ArrayForm_InvalidSectiontranslator/tocwconfig— End-to-EndTestSyslogConfigRequirements
Before commiting your code, please do the following steps.
make fmtandmake fmt-shmake lintIntegration Tests
To run integration tests against this PR, add the
ready for testinglabel.