Blocky: REST Query Obfuscation With Log Privacy Enabled
When using Blocky with log privacy enabled, you might encounter an issue where the responses to the /api/query endpoint are obfuscated. This behavior occurs because the REST server implementation utilizes the same formatter as the logger, leading to unintended masking of the query results. This article delves into the specifics of this issue, providing insights and potential solutions.
Understanding the Issue
When log privacy is enabled in Blocky, the responses to the /api/query endpoint become obfuscated. This is primarily because the REST server implementation shares the same formatter as the logger. This means that if privacy settings are activated, sensitive information in the query responses is masked, which can hinder debugging and monitoring efforts.
To reproduce this issue, you can use a minimal configuration file, as shown below:
ports:
http: 8080
upstreams:
groups:
default:
- 1.1.1.1
log:
privacy: true
With this configuration, any query made to the /api/query endpoint will return an obfuscated response. For example, a query for example.com might yield the following result:
$ curl -s localhost:8080/api/query --data '{"query":"example.com","type":"A"}' | jq
{
"reason": "RESOLVED (tcp+udp:1.1.1.1)",
"response": "* (**.***.***.**), * (**.***.*.***), * (**.***.**.***), * (**.***.*.***), * (**.***.***.**), * (**.***.**.***)",
"responseType": "RESOLVED",
"returnCode": "NOERROR"
}
As you can see, the actual IP addresses in the response are masked with asterisks, making it difficult to discern the true resolution results. This obfuscation is a direct consequence of the log privacy setting affecting the REST server's response formatting.
The core of the problem lies in how Blocky's REST server implementation reuses the same formatter as the logger. Specifically, the api_interface_impl.go file and the common.go file within the Blocky repository are implicated:
By examining these files, it becomes clear that the formatting logic intended for log messages is also applied to the REST API responses, leading to the observed obfuscation.
Root Cause Analysis
The root cause of this issue is the shared formatting logic between the logging mechanism and the REST API responses. When log privacy is enabled, the formatting functions mask IP addresses and other potentially sensitive information to prevent them from being written to the logs. However, this same masking is inadvertently applied to the API responses, which are intended to provide detailed, un-obfuscated information for monitoring and debugging purposes.
Specifically, the util/common.go file contains functions that handle the formatting of log messages. These functions are designed to obfuscate IP addresses by replacing parts of them with asterisks. When the REST server implementation in api/api_interface_impl.go uses these same functions to format its responses, the obfuscation is applied, regardless of whether the output is going to a log file or an API response.
This design choice creates an unintended side effect where enabling log privacy also impacts the usability of the API. While privacy is crucial for log data, it is equally important for API responses to remain clear and detailed for effective system monitoring and troubleshooting.
Potential Solutions
To address this issue, there are several potential solutions that can be implemented within Blocky. Each solution aims to decouple the formatting logic for logs and API responses, ensuring that privacy settings do not interfere with the clarity of API data.
1. Separate Formatting Functions
The most straightforward solution is to create separate formatting functions for log messages and API responses. This would involve duplicating the existing formatting logic and modifying the API-specific functions to exclude the obfuscation steps. By maintaining distinct formatting functions, the log privacy setting would only affect the logs, leaving the API responses untouched.
This approach ensures that the privacy of log data is maintained without compromising the usefulness of the API. The downside is the potential for code duplication, which can increase maintenance overhead. However, the benefits of clear API responses often outweigh this cost.
2. Conditional Formatting
Another approach is to introduce conditional formatting within the existing formatting functions. This could be achieved by adding a flag or context that indicates whether the formatting is being applied to a log message or an API response. The obfuscation logic would then be conditionally applied based on this flag.
For example, the formatting functions in util/common.go could be modified to check a context variable before masking IP addresses. If the context indicates that the output is for an API response, the masking would be skipped. This approach minimizes code duplication but requires careful management of the context to ensure that the correct formatting is applied.
3. Introduce a New Configuration Option
A more flexible solution could involve introducing a new configuration option that specifically controls the obfuscation of API responses. This option would allow users to independently configure the privacy settings for logs and API data. For instance, a user might choose to enable log privacy while disabling obfuscation for API responses.
This approach provides the greatest degree of control but also adds complexity to the configuration. It requires careful consideration of the user interface and documentation to ensure that the new option is clear and easy to use.
4. Implement a DTO (Data Transfer Object) Pattern
Implementing a Data Transfer Object (DTO) pattern can help in decoupling the data representation for logging and API responses. This involves creating separate DTOs for log messages and API responses, each with its own formatting logic. The DTO for log messages would include obfuscation, while the DTO for API responses would not.
This approach ensures a clear separation of concerns and avoids the shared formatting issue. It may require more significant code changes but results in a cleaner and more maintainable architecture.
Implementing a Solution: A Practical Example
Let's delve deeper into the first solution: creating separate formatting functions. This example illustrates how you might modify the Blocky codebase to address the obfuscation issue.
Step 1: Duplicate Formatting Functions
First, duplicate the existing formatting functions in util/common.go. Rename the duplicates to indicate they are for API responses. For example, if there's a function called ObfuscateIP, create a duplicate named FormatIPForAPI.
Step 2: Modify API-Specific Functions
Modify the API-specific formatting functions to exclude the obfuscation logic. This typically involves removing the parts of the code that replace IP address segments with asterisks.
Step 3: Update REST Server Implementation
In api/api_interface_impl.go, update the code to use the new API-specific formatting functions when constructing the API responses. This ensures that the responses are formatted without obfuscation.
Step 4: Test the Changes
After implementing the changes, thoroughly test the API endpoints with log privacy enabled to verify that the responses are no longer obfuscated. Also, test the logging functionality to ensure that log messages are still correctly obfuscated.
Conclusion
The obfuscation of REST query responses when log privacy is enabled in Blocky is a significant issue that can hinder effective monitoring and debugging. Understanding the root cause—the shared formatting logic between logs and API responses—is crucial for implementing the right solution.
By creating separate formatting functions, introducing conditional formatting, adding a new configuration option, or implementing a DTO pattern, Blocky can be modified to provide clear, detailed API responses while maintaining the privacy of log data. The practical example of duplicating formatting functions demonstrates a straightforward approach to resolving this issue.
Addressing this issue enhances the usability of Blocky and ensures that users can effectively manage their DNS configurations without compromising either privacy or clarity.
For further information on DNS privacy and security, you can explore resources like the Internet Engineering Task Force (IETF), which provides standards and best practices for internet protocols.