Tyk Open source - Mutual TLS - Problem with Chrome on Android

Hi all!

I encountered some strange behavior when I have configured an API that use Mutual TLS.
This is the situation:

  • Under Tyk I’ve an API configured with the Mutual TLS
  • I’ve a web app that call some other APIs configured under Tyk. It doesn’t call the one using the Mutual TLS (used only for a communication backend to backend)
  • If I call the web app from Chrome under Android, the browser ask me to select a client certificate in order to authenticate with the API Gateway. If I click on the “Undo” button, it all works fine: the app work properly

It seems the problem raise up only with Android and Chrome: if I use Firefox on Android it works fine and the certificate is not requested. It’s all ok also with Windows devices anche Chrome.

I suppose I’m missing something, maybe there’s an error in some configuration, but I don’t figure it out.

Here some configurations that I use.

Thank you for the help!

Regards,
Emanuele

Tyk Version: Open Source v4.0.3
OS Version: CentOS 7

tyk.conf used:

{
  "listen_port": 443,
  "secret": "asecret",
  "template_path": "/opt/tyk-gateway/templates",
  "use_db_app_configs": false,
  "app_path": "/opt/tyk-gateway/apps",
  "middleware_path": "/opt/tyk-gateway/middleware",
  "enable_websockets": true,
  "log_level": "debug",
  "storage": {
    "type": "redis",
    "host": "localhost",
    "port": xxxx,
    "username": "yyyy",
    "password": "zzzz",
    "database": 0,
    "optimisation_max_idle": 2000,
    "optimisation_max_active": 4000
  },
  "enable_analytics": false,
  "analytics_config": {
    "type": "",
    "ignored_ips": [],
    "normalise_urls": {
      "enabled": true,
      "normalise_uuids": true,
      "normalise_numbers": true,
      "custom_patterns": []
    }
  },
  "health_check": {
    "enable_health_checks": false,
    "health_check_value_timeouts": 60
  },
  "dns_cache": {
    "enabled": false,
    "ttl": 3600
  },
  "allow_master_keys": false,
  "policies": {
    "policy_source": "file",
    "policy_record_name": "policies/policies.json"
  },
  "hash_keys": true,
  "suppress_redis_signal_reload": false,
  "close_connections": false,
  "enable_non_transactional_rate_limiter": true,
  "enable_sentinel_rate_limiter": false,
  "local_session_cache": {
    "disable_cached_session_state": false
  },
  "uptime_tests": {
    "disable": false,
    "config": {
      "enable_uptime_analytics": false,
      "failure_trigger_sample_size": 2,
      "time_wait": 10,
      "checker_pool_size": 50
    }
  },
  "http_server_options": {
    "enable_http2": true,
    "enable_websockets": true,
    "use_ssl": true,
    "ssl_insecure_skip_verify": true,    
    "certificates": [
     {
      "domain_name": "*",
      "cert_file": "/afolder/mypublic.pem",
      "key_file": "/afolder/myprivate.key"
     }
    ]
  },
  "hostname": "",
  "enable_custom_domains": true,
  "proxy_enable_http2": true,
  "enable_jsvm": true,
  "oauth_redirect_uri_separator": ";",
  "coprocess_options": {
    "enable_coprocess": false,
    "coprocess_grpc_server": "",
    "python_path_prefix": "/opt/tyk-gateway"
  },
  "pid_file_location": "./tyk-gateway.pid",
  "allow_insecure_configs": true,
  "public_key_path": "",
  "close_idle_connections": false,
  "allow_remote_config": false,
  "enable_bundle_downloader": true,
  "bundle_base_url": "",
  "global_session_lifetime": 100,
  "force_global_session_lifetime": false,
  "max_idle_connections_per_host": 500
}

The API with Mutual TLS configuration:

{
  "name": "My Mutual TLS",
  "slug": "my-mutual-tls",
  "listen_port": 0,
  "protocol": "",
  "enable_proxy_protocol": false,
  "api_id": "my-mutual-tls",
  "org_id": "1",
  "use_keyless": true,
  "use_oauth2": false,
  "use_openid": false,
  "openid_options": {
    "providers": null,
    "segregate_by_client": false
  },
  "oauth_meta": {
    "allowed_access_types": null,
    "allowed_authorize_types": null,
    "auth_login_redirect": ""
  },
  "auth": {
    "use_param": false,
    "param_name": "",
    "use_cookie": false,
    "cookie_name": "",
    "auth_header_name": "myheader",
    "use_certificate": false,
    "validate_signature": false,
    "signature": {
      "algorithm": "",
      "header": "",
      "secret": "",
      "allowed_clock_skew": 0,
      "error_code": 0,
      "error_message": ""
    }
  },
  "auth_configs": null,
  "use_basic_auth": false,
  "basic_auth": {
    "disable_caching": false,
    "cache_ttl": 0,
    "extract_from_body": false,
    "body_user_regexp": "",
    "body_password_regexp": ""
  },
  "use_mutual_tls_auth": true,
  "client_certificates": [
    "the-client-certificate-id"
  ],
  "upstream_certificates": null,
  "pinned_public_keys": null,
  "enable_jwt": false,
  "use_standard_auth": false,
  "use_go_plugin_auth": false,
  "enable_coprocess_auth": false,
  "jwt_signing_method": "",
  "jwt_source": "",
  "jwt_identity_base_field": "",
  "jwt_client_base_field": "",
  "jwt_policy_field_name": "",
  "jwt_default_policies": null,
  "jwt_issued_at_validation_skew": 0,
  "jwt_expires_at_validation_skew": 0,
  "jwt_not_before_validation_skew": 0,
  "jwt_skip_kid": false,
  "jwt_scope_to_policy_mapping": null,
  "jwt_scope_claim_name": "",
  "notifications": {
    "shared_secret": "",
    "oauth_on_keychange_url": ""
  },
  "enable_signature_checking": false,
  "hmac_allowed_clock_skew": 0,
  "hmac_allowed_algorithms": null,
  "request_signing": {
    "is_enabled": false,
    "secret": "",
    "key_id": "",
    "algorithm": "",
    "header_list": null,
    "certificate_id": "",
    "signature_header": ""
  },
  "base_identity_provided_by": "",
  "definition": {
    "location": "header",
    "key": "x-api-version",
    "strip_path": false
  },
  "version_data": {
    "not_versioned": true,
    "default_version": "",
    "versions": {
      "Default": {
        "name": "Default",
        "expires": "",
        "paths": {
          "ignored": null,
          "white_list": null,
          "black_list": null
        },
        "use_extended_paths": true,
        "extended_paths": {},
        "global_headers": null,
        "global_headers_remove": null,
        "global_response_headers": null,
        "global_response_headers_remove": null,
        "ignore_endpoint_case": false,
        "global_size_limit": 0,
        "override_target": ""
      }
    }
  },
  "uptime_tests": {
    "check_list": null,
    "config": {
      "expire_utime_after": 0,
      "service_discovery": {
        "use_discovery_service": false,
        "query_endpoint": "",
        "use_nested_query": false,
        "parent_data_path": "",
        "data_path": "",
        "port_data_path": "",
        "target_path": "",
        "use_target_list": false,
        "cache_timeout": 0,
        "endpoint_returns_list": false
      },
      "recheck_wait": 0
    }
  },
  "proxy": {
    "preserve_host_header": false,
    "listen_path": "/my/mutual/TLS",
    "target_url": "https://myaddress.com:1234/api/mymutaltls/",
    "disable_strip_slash": false,
    "strip_listen_path": true,
    "enable_load_balancing": false,
    "target_list": null,
    "check_host_against_uptime_tests": false,
    "service_discovery": {
      "use_discovery_service": false,
      "query_endpoint": "",
      "use_nested_query": false,
      "parent_data_path": "",
      "data_path": "",
      "port_data_path": "",
      "target_path": "",
      "use_target_list": false,
      "cache_timeout": 0,
      "endpoint_returns_list": false
    },
    "transport": {
      "ssl_insecure_skip_verify": true,
      "ssl_ciphers": null,
      "ssl_min_version": 0,
      "ssl_force_common_name_check": false,
      "proxy_url": ""
    }
  },
  "disable_rate_limit": true,
  "disable_quota": true,
  "custom_middleware": {
    "pre": null,
    "post": null,
    "post_key_auth": null,
    "auth_check": {
      "name": "",
      "path": "",
      "require_session": false,
      "raw_body_only": false
    },
    "response": null,
    "driver": "",
    "id_extractor": {
      "extract_from": "",
      "extract_with": "",
      "extractor_config": null
    }
  },
  "custom_middleware_bundle": "",
  "cache_options": {
    "cache_timeout": 0,
    "enable_cache": false,
    "cache_all_safe_requests": false,
    "cache_response_codes": null,
    "enable_upstream_cache_control": false,
    "cache_control_ttl_header": "",
    "cache_by_headers": null
  },
  "session_lifetime": 0,
  "active": true,
  "internal": false,
  "auth_provider": {
    "name": "",
    "storage_engine": "",
    "meta": null
  },
  "session_provider": {
    "name": "",
    "storage_engine": "",
    "meta": null
  },
  "event_handlers": {
    "events": null
  },
  "enable_batch_request_support": false,
  "enable_ip_whitelisting": false,
  "allowed_ips": null,
  "enable_ip_blacklisting": false,
  "blacklisted_ips": null,
  "dont_set_quota_on_create": false,
  "expire_analytics_after": 0,
  "response_processors": null,
  "CORS": {
    "enable": true,
    "allowed_origins": [
      "https://myaddress.com"
    ],
    "allowed_methods": [
      "GET",
      "POST",
      "PUT",
      "DELETE",
      "HEAD",
      "OPTIONS",
      "PATCH",
      "TRACE"
    ],
    "allowed_headers": [
      "content-type",
      "myheader",
      "authentication",
      "is-not-cancelable",
      "my-culture",
      "Authorization",
      "x-requested-with",
      "X-Requested-With"
    ],
    "exposed_headers": [],
    "allow_credentials": false,
    "max_age": 99,
    "options_passthrough": false,
    "debug": false
  },
  "domain": "",
  "certificates": null,
  "do_not_track": false,
  "tags": null,
  "enable_context_vars": false,
  "config_data": null,
  "tag_headers": null,
  "global_rate_limit": {
    "rate": 0,
    "per": 0
  },
  "strip_auth_data": false,
  "enable_detailed_recording": false,
  "graphql": {
    "enabled": false,
    "execution_mode": "",
    "schema": "",
    "type_field_configurations": null,
    "playground": {
      "enabled": false,
      "path": ""
    }
  }
}

Hi @e.dabove and welcome to the community.

I know there is a weird behavior when the only API loaded on the gateway is one with mTLS. But this doesn’t sound like the case.

If you don’t mind me asking what is the composition of your APIs? How many are keyless, using auths, etc.

The behavior sounds very strange indeed especially since it’s only occurring in chrome on mobile. Maybe you could try clearing the browser cache as a start.

If that doesn’t work maybe enabling debug logs would reveal something interesting. I may have to try it out myself.

Hi @Olu thank you for your reply.

Following a typical API configuration that I use.
Consider that I’ve about 10 normal APIs and one with Mutual TLS.

I’ve already cleared tha browser cache, but the problem still remains.

I’ll try to enable debug logs in order to discover something news.
I’ll post here the results.

Thanks!

Regards,
Emanuele

“Normal” API configuration:

{
  "name": "My Classic API",
  "slug": "my-classic-api",
  "listen_port": 0,
  "protocol": "",
  "enable_proxy_protocol": false,
  "api_id": "my-classic-api",
  "org_id": "1",
  "use_keyless": false,
  "use_oauth2": false,
  "use_openid": false,
  "openid_options": {
    "providers": null,
    "segregate_by_client": false
  },
  "oauth_meta": {
    "allowed_access_types": null,
    "allowed_authorize_types": null,
    "auth_login_redirect": ""
  },
  "auth": {
    "use_param": false,
    "param_name": "",
    "use_cookie": false,
    "cookie_name": "",
    "auth_header_name": "my-header-check",
    "use_certificate": false,
    "validate_signature": false,
    "signature": {
      "algorithm": "",
      "header": "",
      "secret": "",
      "allowed_clock_skew": 0,
      "error_code": 0,
      "error_message": ""
    }
  },
  "auth_configs": null,
  "use_basic_auth": false,
  "basic_auth": {
    "disable_caching": false,
    "cache_ttl": 0,
    "extract_from_body": false,
    "body_user_regexp": "",
    "body_password_regexp": ""
  },
  "use_mutual_tls_auth": false,
  "client_certificates": null,
  "upstream_certificates": null,
  "pinned_public_keys": null,
  "enable_jwt": false,
  "use_standard_auth": false,
  "use_go_plugin_auth": false,
  "enable_coprocess_auth": false,
  "jwt_signing_method": "",
  "jwt_source": "",
  "jwt_identity_base_field": "",
  "jwt_client_base_field": "",
  "jwt_policy_field_name": "",
  "jwt_default_policies": null,
  "jwt_issued_at_validation_skew": 0,
  "jwt_expires_at_validation_skew": 0,
  "jwt_not_before_validation_skew": 0,
  "jwt_skip_kid": false,
  "jwt_scope_to_policy_mapping": null,
  "jwt_scope_claim_name": "",
  "notifications": {
    "shared_secret": "",
    "oauth_on_keychange_url": ""
  },
  "enable_signature_checking": false,
  "hmac_allowed_clock_skew": 0,
  "hmac_allowed_algorithms": null,
  "request_signing": {
    "is_enabled": false,
    "secret": "",
    "key_id": "",
    "algorithm": "",
    "header_list": null,
    "certificate_id": "",
    "signature_header": ""
  },
  "base_identity_provided_by": "",
  "definition": {
    "location": "header",
    "key": "x-api-version",
    "strip_path": false
  },
  "version_data": {
    "not_versioned": true,
    "default_version": "",
    "versions": {
      "Default": {
        "name": "Default",
        "expires": "",
        "paths": {
          "ignored": null,
          "white_list": null,
          "black_list": null
        },
        "use_extended_paths": true,
        "extended_paths": {},
        "global_headers": null,
        "global_headers_remove": null,
        "global_response_headers": null,
        "global_response_headers_remove": null,
        "ignore_endpoint_case": false,
        "global_size_limit": 0,
        "override_target": ""
      }
    }
  },
  "uptime_tests": {
    "check_list": null,
    "config": {
      "expire_utime_after": 0,
      "service_discovery": {
        "use_discovery_service": false,
        "query_endpoint": "",
        "use_nested_query": false,
        "parent_data_path": "",
        "data_path": "",
        "port_data_path": "",
        "target_path": "",
        "use_target_list": false,
        "cache_timeout": 0,
        "endpoint_returns_list": false
      },
      "recheck_wait": 0
    }
  },
  "proxy": {
    "preserve_host_header": false,
    "listen_path": "/my/classicapi/",
    "target_url": "https://my.domain.something.com:6789/my/api/",
    "disable_strip_slash": false,
    "strip_listen_path": true,
    "enable_load_balancing": false,
    "target_list": null,
    "check_host_against_uptime_tests": false,
    "service_discovery": {
      "use_discovery_service": false,
      "query_endpoint": "",
      "use_nested_query": false,
      "parent_data_path": "",
      "data_path": "",
      "port_data_path": "",
      "target_path": "",
      "use_target_list": false,
      "cache_timeout": 0,
      "endpoint_returns_list": false
    },
    "transport": {
      "ssl_insecure_skip_verify": false,
      "ssl_ciphers": null,
      "ssl_min_version": 0,
      "ssl_force_common_name_check": false,
      "proxy_url": ""
    }
  },
  "disable_rate_limit": true,
  "disable_quota": true,
  "custom_middleware": {
    "pre": null,
    "post": null,
    "post_key_auth": null,
    "auth_check": {
      "name": "",
      "path": "",
      "require_session": false,
      "raw_body_only": false
    },
    "response": null,
    "driver": "",
    "id_extractor": {
      "extract_from": "",
      "extract_with": "",
      "extractor_config": null
    }
  },
  "custom_middleware_bundle": "",
  "cache_options": {
    "cache_timeout": 0,
    "enable_cache": false,
    "cache_all_safe_requests": false,
    "cache_response_codes": null,
    "enable_upstream_cache_control": false,
    "cache_control_ttl_header": "",
    "cache_by_headers": null
  },
  "session_lifetime": 0,
  "active": true,
  "internal": false,
  "auth_provider": {
    "name": "",
    "storage_engine": "",
    "meta": null
  },
  "session_provider": {
    "name": "",
    "storage_engine": "",
    "meta": null
  },
  "event_handlers": {
    "events": null
  },
  "enable_batch_request_support": false,
  "enable_ip_whitelisting": false,
  "allowed_ips": null,
  "enable_ip_blacklisting": false,
  "blacklisted_ips": null,
  "dont_set_quota_on_create": false,
  "expire_analytics_after": 0,
  "response_processors": null,
  "CORS": {
    "enable": true,
    "allowed_origins": [
      "http://localhost:4200",
      "https://my.domain.something.com"
    ],
    "allowed_methods": [
      "GET",
      "POST",
      "PUT",
      "DELETE",
      "HEAD",
      "OPTIONS",
      "PATCH",
      "TRACE"
    ],
    "allowed_headers": [
      "content-type",
      "my-header-check",
      "authentication",
      "is-not-cancelable",
      "my-culture",
      "Authorization",
      "x-requested-with",
      "X-Requested-With"
    ],
    "exposed_headers": [],
    "allow_credentials": false,
    "max_age": 99,
    "options_passthrough": false,
    "debug": false
  },
  "domain": "",
  "certificates": null,
  "do_not_track": false,
  "tags": null,
  "enable_context_vars": false,
  "config_data": null,
  "tag_headers": null,
  "global_rate_limit": {
    "rate": 0,
    "per": 0
  },
  "strip_auth_data": false,
  "enable_detailed_recording": false,
  "graphql": {
    "enabled": false,
    "execution_mode": "",
    "schema": "",
    "type_field_configurations": null,
    "playground": {
      "enabled": false,
      "path": ""
    }
  }
}

@e.dabove

Did you get any information in the logs? Maybe an error or warning?

I could not reproduce it on my end. @Pete has a suggestion that the issue could be SNI related

However, could you check that you haven’t added any additional client certificates in any other API definition file?

Can you confirm if you have the right certificate ID in storage? You can use the to call the Gateway Cert API

{{GATEWAY_ENDPOINT}}/tyk/{{CERTIFICATE_ID}}/