Skip to content
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

Expose /env and /config for Configuration reporting #6652

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

letzya
Copy link
Contributor

@letzya letzya commented Oct 18, 2024

User description

Description

Related Issue

Motivation and Context

How This Has Been Tested

These endpoints are a control port and a dedicated API key

// /env
~ » curl  -H "X-Tyk-Authorization: topsecret" 'http://localhost:8181/env' | jq '.[]' |grep -i cache

// /env?env=<env name>
~ » curl -s  -H "X-Tyk-Authorization: topsecret" 'http://localhost:8181/env?env=TYK_GW_CACHESTORAGE_PORT' | jq .
{
  "config_field": "cache_storage.port",
  "env": "TYK_GW_CACHESTORAGE_PORT",
  "value": "0",
  "obfuscated": false
}

// /config
~ » curl -s -H "X-Tyk-Authorization: topsecret" 'http://localhost:8181/config' | jq .cache_storage
{
  "type": "",
  "host": "",
  "port": 0,
  "hosts": {},
  "addrs": null,
  "master_name": "",
  "sentinel_password": "",
  "username": "",
  "password": "",
  "database": 0,
  "optimisation_max_idle": 0,
  "optimisation_max_active": 0,
  "timeout": 0,
  "enable_cluster": false,
  "use_ssl": false,
  "ssl_insecure_skip_verify": false,
  "ca_file": "",
  "cert_file": "",
  "key_file": "",
  "tls_max_version": "",
  "tls_min_version": ""
}

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Refactoring or add test (improvements in base code or adding test coverage to functionality)

Checklist

  • I ensured that the documentation is up to date
  • I explained why this PR updates go.mod in detail with reasoning why it's required
  • I would like a code coverage CI quality gate exception and have explained why

PR Type

Enhancement


Description

  • Introduced a Configuration Reflection API for monitoring/debugging.

  • Added /config and /env endpoints with secure access.

  • Integrated structviewer library for configuration handling.

  • Obfuscated sensitive fields in configuration for security.


Changes walkthrough 📝

Relevant files
Enhancement
config.go
Added Configuration Reflection API configuration.               

config/config.go

  • Added ConfigurationReflectionCfg struct for API configuration.
  • Integrated new configuration fields for enabling and securing the API.
  • Obfuscated sensitive fields like APIKey, Secret, and NodeSecret.
  • +29/-3   
    server.go
    Added Configuration Reflection API endpoints.                       

    gateway/server.go

  • Implemented /config and /env endpoints for configuration reflection.
  • Added authConfigurationAPI middleware for secure API access.
  • Integrated structviewer for handling configuration and environment
    variables.
  • +71/-0   
    Dependencies
    go.mod
    Added `structviewer` dependency.                                                 

    go.mod

    • Added structviewer library dependency.
    +1/-0     
    go.sum
    Updated dependencies checksums.                                                   

    go.sum

    • Updated checksums for structviewer library.
    +2/-0     

    Need help?
  • Type /help how to ... in the comments thread for any questions about PR-Agent usage.
  • Check out the documentation for more information.
  • @letzya letzya force-pushed the letzya-config-endpoint branch from f157030 to 5e8d5d7 Compare October 18, 2024 17:20
    @letzya letzya force-pushed the letzya-config-endpoint branch from 5f84619 to 52d9d9d Compare October 20, 2024 19:50
    @letzya letzya requested a review from yurisasuke February 19, 2025 18:47
    @yurisasuke yurisasuke marked this pull request as ready for review February 20, 2025 06:49
    Copy link
    Contributor

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 No relevant tests
    🔒 Security concerns

    Sensitive information exposure:
    The APIKey for the configuration reflection API is critical for securing the /config and /env endpoints. Ensure that this key is not logged, exposed, or hardcoded in a way that could lead to unauthorized access.

    ⚡ Recommended focus areas for review

    Possible Misconfiguration

    The ConfigurationReflectionCfg struct introduces new fields such as Enable, APIPort, and APIKey. Ensure that these fields are properly validated and documented to avoid potential misconfigurations, especially since they control sensitive debugging endpoints.

    type ConfigurationReflectionCfg struct {
    
    	// Enable controls the availability of HTTP endpoints for monitoring and debugging Tyk Gateway.
    	// These endpoints provide critical system information and are disabled by default for security reasons.
    	// Access to these endpoints requires a secret, defined in the `security.secret` configuration field.
    	// Available endpoints include:
    	// * /config - a read-only HTTP endpoint that returns the current config keys and values in JSON notation.
    	// * /env    - a read-only HTTP endpoint that returns the current config keys and values in an environment variable
    	//   format
    	Enable bool `json:"enable"`
    
    	// Ensure the Gateway configuration port is set
    	// The Configuration API runs on a separate port, so you can secure this port behind a firewall.
    	APIPort int `json:"api_port"`
    
    	// This should be changed as soon as Tyk is installed on your system.
    	// This value is used in when using Tyk Gateway Config APIs. It should be passed along as the X-Tyk-Authorization
    	// header in any requests made. Tyk assumes that you are sensible enough not to expose the management endpoints
    	// publicly and to keep this configuration value to yourself.
    	APIKey string `json:"api_key" structviewer:"obfuscate"`
    Security Concern

    The authConfigurationAPI function relies on a shared secret (APIKey) for authentication. Ensure that this key is securely stored and not exposed in logs or error messages to prevent unauthorized access to sensitive endpoints.

    // authConfigurationAPI will ensure that the accessor of the HTTP API has the
    // correct security credentials - this is a shared secret between the
    // client and the owner and is set in the settings. This should
    // never be made public!
    func authConfigurationAPI(next http.Handler, apiKey string) http.Handler {
    	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    		tykAuthKey := r.Header.Get(header.XTykAuthorization)
    		if tykAuthKey != apiKey {
    			errMsg := "Attempted administrative access to cnfiguration reflection with invalid or missing key!"
    			mainLog.Error(errMsg)
    			doJSONWrite(w, http.StatusUnauthorized, apiError(errMsg))
    			return
    		}
    		next.ServeHTTP(w, r)
    	})
    Error Handling

    The loadConfigurationAPI function has multiple error conditions, such as missing ControlAPIPort or APIKey. Ensure these errors are logged clearly and provide actionable guidance for users to fix the configuration.

    func (gw *Gateway) loadConfigurationAPI(muxer *mux.Router) error {
    
    	config := gw.GetConfig()
    
    	// Check if enabled
    	if config.ConfigurationReflection.Enable != true {
    		mainLog.Info("configuration reflection API is disabled in the configuration. Please enable it if you need to use it")
    		return nil
    	}
    	mainLog.Debug("configuration reflection API is enabled")
    
    	if config.ControlAPIPort == 0 {
    		return fmt.Errorf("configuration reflection API requires a dedicated control port. Please set `control_api_port` in order to use it")
    	}
    	/*
    		port := config.ControlAPIPort
    		if port == 0 {
    			port = config.ConfigurationReflection.APIPort
    		}
    		// Check the port
    		if port == 0 {
    			return fmt.Errorf("configuration reflection API requires a separate port. Please set it in order to use it")
    		}
    		mainLog.Info("configuration reflection API Port is set to %d", port)
    	*/
    
    	// Check the API key
    	if config.ConfigurationReflection.APIKey == "" {
    		return fmt.Errorf("configuration reflection API port is not set. Please set it in order to use it")
    	}
    
    	var structviewerCfg = &structviewer.Config{
    		Object:        config,
    		ParseComments: false,
    	}
    	const GW_ENV_PREFIX = "TYK_GW_"
    	v, err := structviewer.New(structviewerCfg, GW_ENV_PREFIX)
    	if err != nil {
    		return err
    	}
    
    	// TODO: Update swagger.yaml of the gateway
    	// Define routes and middleware specific to the Config API
    	muxer.Handle("/config", authConfigurationAPI(http.HandlerFunc(v.ConfigHandler), config.ConfigurationReflection.APIKey))
    	muxer.Handle("/env", authConfigurationAPI(http.HandlerFunc(v.EnvsHandler), config.ConfigurationReflection.APIKey))
    
    	mainLog.Info("--> Configuration reflection API is available on control port")
    
    	return nil
    }

    Copy link
    Contributor

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Security
    Validate API key strength for security

    Add a check to ensure that the ConfigurationReflection.APIKey is not a default or
    weak value to enhance security and prevent unauthorized access.

    gateway/server.go [2068-2070]

    -if config.ConfigurationReflection.APIKey == "" {
    -    return fmt.Errorf("configuration reflection API port is not set. Please set it in order to use it")
    +if config.ConfigurationReflection.APIKey == "" || config.ConfigurationReflection.APIKey == "default_key" {
    +    return fmt.Errorf("configuration reflection API key is not set or is using a default value. Please set a secure key in order to use it")
     }
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion enhances security by ensuring the API key is not set to a default or weak value, which is critical for preventing unauthorized access. It is highly relevant and directly improves the robustness of the new code.

    High
    Sanitize and validate authorization header

    Ensure that the authConfigurationAPI middleware sanitizes and validates the
    X-Tyk-Authorization header to prevent injection attacks.

    gateway/server.go [2097]

    -tykAuthKey := r.Header.Get(header.XTykAuthorization)
    +tykAuthKey := strings.TrimSpace(r.Header.Get(header.XTykAuthorization))
    +if !isValidAuthKey(tykAuthKey) {
    +    errMsg := "Invalid or malformed authorization key!"
    +    mainLog.Error(errMsg)
    +    doJSONWrite(w, http.StatusUnauthorized, apiError(errMsg))
    +    return
    +}
    Suggestion importance[1-10]: 9

    __

    Why: Sanitizing and validating the X-Tyk-Authorization header prevents injection attacks, which is a critical security improvement. The suggestion is highly relevant and directly addresses a potential vulnerability.

    High
    Add rate-limiting to prevent abuse

    Add a rate-limiting mechanism to the authConfigurationAPI middleware to prevent
    brute force attacks on the API key.

    gateway/server.go [2098-2103]

     if tykAuthKey != apiKey {
    -    errMsg := "Attempted administrative access to cnfiguration reflection with invalid or missing key!"
    +    errMsg := "Attempted administrative access to configuration reflection with invalid or missing key!"
         mainLog.Error(errMsg)
    +    // Add rate-limiting logic here to prevent brute force attacks
         doJSONWrite(w, http.StatusUnauthorized, apiError(errMsg))
         return
     }
    Suggestion importance[1-10]: 8

    __

    Why: Adding rate-limiting to the middleware would mitigate brute force attacks, significantly improving the security of the API. This is a valuable enhancement to the existing code.

    Medium
    General
    Log control port for debugging

    Log the control port value explicitly when the configuration reflection API is
    enabled to aid in debugging and monitoring.

    gateway/server.go [2087]

    -mainLog.Info("--> Configuration reflection API is available on control port")
    +mainLog.Infof("--> Configuration reflection API is available on control port %d", config.ControlAPIPort)
    Suggestion importance[1-10]: 7

    __

    Why: Logging the control port explicitly when the configuration reflection API is enabled aids in debugging and monitoring. This is a useful enhancement for operational visibility, though it does not address a critical issue.

    Medium

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    1 participant