TeslaMate 是一款特斯拉汽车专用的增强工具,使用了破解的官方 App 的 API,将车机记录的大量信息进行了导出和展示,极大的增强了特斯拉车主的使用体验。
但是由于一些原因,中国特斯拉的汽车的数据都是存放在中国大陆,且不允许境外 IP 访问。因此,中国车主想要使用 TeslaMate,则必须在境内部署相关的服务。
不过,境内部署虽然可以解决访问 Tesla API 的问题,却也带来了新的问题,就是调用境外 API 的时候会出现错误,导致部分功能无法使用。
具体来说,TeslaMate 使用了 OpenStreetMap 提供的 API,用来将经纬度坐标转换为地名。然而,OpenStreetMap 的域名在境内是无法访问的,因此会导致解析坐标点的时候出现失败,导致在 Grafana 的 Drives(行程) 面板不显示行程的起止地址。
为了解决这个问题,我首先想到了设置 http_proxy 等环境变量的方式。但是搜索之后发现,似乎 TeslaMate 并不会读区这几个环境变量,因此无法使用这种方式强制走代理访问相关 API。而且,由于 Tesla 本身的 API 不能走代理访问,必须使用中国 IP 访问,因此如果要使用这种方式的话,还需要配置 Proxy 的分流策略,非常麻烦。
综合考虑各种方案,我最终选择了 sniproxy 的方式。sniproxy 是允许在不解密流量本身的情况下,进行 SSL/TLS 流量转发的方案。使用 sniproxy 时,只需要将目标域名的 IP 强制指向 sniproxy 的 IP 地址(如修改 /etc/hosts),既可以完成代理。
方案一:直接在本地运行 sniproxy
- 优点:不占用母鸡的 443 端口,不影响现有服务
- 缺点:配置较为繁琐,需要两个额外的容器互相配合
考虑到搭建 sniproxy 需要占用一个 IP 的 443 端口,因此我决定在 docker compose 内增加 sniproxy 的相关容器,来做到不占用母鸡的 443 端口的目的。由于 sniproxy 是运行在 TeslaMate 同一台机器上的,因此还需要一个额外的代理来将流量转发出去。这一步有很多方案,使用 warp+、ss、v2 等均可实现,我选择使用 v2 的方案。
最终修改后的 docker-compose.yaml 新增的内容如下:
sniproxy:
image: sniproxy
restart: always
command: ./sniproxy
depends_on:
- xray
networks:
default:
aliases:
- nominatim.openstreetmap.org
xray:
image: teddysun/xray:latest
restart: always
volumes:
- ./xray:/etc/xray
其中,sniproxy 镜像是对 https://github.com/XIU2/SNIProxy 项目的简单包装,将配置文件写在了镜像内。配置内容如下:
# 监听端口(注意需要引号)
listen_addr: ":443"
# 可选:启用 Socks5 前置代理
enable_socks5: true
# 可选:配置 Socks5 代理地址
socks_addr: xray:40000
# 可选:允许所有域名(会忽略下面的 rules 列表)
#allow_all_hosts: true
# 可选:仅允许指定域名
rules:
- openstreetmap.org
这里的 socks_addr 为下方 xray 容器内的 socks 代理监听地址。
这段配置的重点在于,sniproxy 容器配置了 network alias。这个选项的作用是,告诉 docker compose,将指定的域名解析到当前容器。因此,加上了这段配置后,docker 容器内解析 nominatim.openstreetmap.org 这个域名,会自动得到 sniproxy 容器的 docker 网络的 IP,达到将 OpenStreetMap 的 API 请求自动导向 sniproxy,进而导向 xray 代理的目的。
方案二:使用单独的服务器部署 sniproxy
- 优点:配置简单,无需额外容器
- 缺点:需要额外的服务器部署 sniproxy,需要占用一个 443 端口
当然,如果有多余的服务器,可以提供一个 443 端口的话,就不用这么麻烦了。直接在服务器上搭建好 sniproxy,然后在 docker-compose.yaml 内的 teslamate 容器配置里增加下面这段就可以了:
extra_hosts:
nominatim.openstreetmap.org: sniproxy_server_ip
这段配置等同于在 teslamate 的 docker 容器的 /etc/hosts 文件内,写入了域名与 IP 的映射关系,因此所有的 OpenStreetMap 相关的 API 请求,都会被导向 sniproxy 的 IP 地址。
P.S. 其实使用 nginx 的 stream 模块,配合 stream_ssl stream_ssl_preread 两个模块,可以做到不影响现有的网站服务的前提下,与 sniproxy 兼容。但是此方法需要对现有的 nginx 配置进行较大的改动,而我本人的 nginx 配置有十几个文件,实在是不想改,因此就放弃了这种方式。
无痛解决 TeslaMate 访问 OpenStreetMap API 的问题 by 桔子小窝 is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.