Published on

如何给本地开启的 API 服务使用上 HTTPS

Authors
  • avatar
    Name
    Adam Liu
    Twitter

一些背景知识

后端开发中,各种 API 服务(比如,Django, Flask, Express.js)启动的时候,一般默认监听的都是127.0.0.1地址。

如果要使用DHCP分配的 IP 地址访问,可以把 host 改为0.0.0.0再启动 API 服务。这样同局域网的主机,比如说前端的同事就可以通过你的 IP 地址访问后端 API 了。

0.0.0.0的意思是分配给改主机的所有可能的 IPv4 地址。IPv6 是::/0

可能分配给主机的 IP 地址是未知的,但是你启动 API 的时候是需要绑定一个 IP 地址的,如果你没有分配到 IP 地址,这个时候127.0.0.1就有用了,或者localhost也可以。

如果启动 API 的时候绑定不是你主机的 IP 会怎样?

你应该会有绑定失败,Django 会抛出这个错误: Error: That IP address can't be assigned to.

所以,0.0.0.0就是有点类似通配符的效果。不需要指定 Hard code 未知的地址。

竟然 0.0.0.0 这么方便,那些 API 框架怎么默认不使用?

我觉得是为了安全,0.0.0.0会直接让你服务器可能直接暴露在公网里面。一般开发过程中都是没有什么认证措施的,这个时候附近有个人端口扫描一下,可能就会泄露重要的资料。


为什么有了这个文章?

近期空闲时间在写 Side project,需要暴露自己的 API 服务在公网,给一个 Twilio 服务回调我的 API。

当然是可以用 ngrok,但是对比于 4 年前,这个服务现在越来越抠门。以前是可以单机多开,而且不限时长,现在玩了付费订阅制之后,限制越来越多。

我的解决方案是:

  1. API 服务监听0.0.0.0端口,给路由器访问。
  2. 路由器做端口转发,让宽带公网 IP 的某个端口映射到本机 API 服务端口。
    1. 为什么不使用路由器 DMZ?
    2. DMZ 会直接暴露本机到公网。只使用端口转发所需的服务,这样安全点。

最后对公网给 Twilio 的 Callback URL 会是这样 http://58.123.123.123:8000/

延伸一下,本地服务 API 服务使用上 HTTPS,而不是直接用本机公网地址?

方案一 AWS API Gateway Proxy

Twilio Callback URL => AWS API Gateway => Your Router Public IP => Router Port forwarding => API (Listen 0.0.0.0)

  1. AWS API Gateway 会给你分配一个 HTTPS 地址,你需要创建一个 Stage 才会有。
  2. 直接创建一个 Resource,然后把所有路由都转发到本机的公网 IP,比如上述的http://58.123.123.123:8000/
  3. 如果你不想使用 AWS API Gateway 分配的域名,你可以使用 AWS ACM 认证域名,然后在 API Gateway 创建一个漂亮的 custom domain。
  4. 重要的是,上述的 1 2 3 都是免费的。如果超出了 AWS free tier 期限,AWS API Gateway 是按需付费,零星的使用量基本上是免费的。
  5. ⚠️ 这个不是全程加密,API Gateway 到本机的 API 服务之间的通信没有使用 HTTPS。

方案二 AWS EC2 Apache 反向代理 + SSH 远程转发(这个是我写这篇文章的时候想到的方法 😂)

Twilio Callback URL => My Wordpress site EC2 Apache Reverse Proxy => SSH Remote forwarding => API (Listen on localhost)

  1. ✅ 这个是全程加密通信。
    1. SaaS 到 Apache 是 HTTPS,我还启用了 Http2.0
    2. Apache 到我本机的 API 服务是 SSH。
  2. Apache 的反向代理可以使用二级域名,然后创建一个 VirtualHost。
  3. SSH 使用的是远程转发。因为要远程主机访问本机服务,本机没有公网 IP。

Apache VirtualHost 代码:

<VirtualHost \_default\_:443>
  # 一个新的二级域名,记得配置DNS,还有SSL/TLS证书
  ServerName xx.adamliu.net
  SSLEngine on
  SSLCertificateFile "/opt/bitnami/apache2/conf/adamliu.net.crt"
  SSLCertificateKeyFile "/opt/bitnami/apache2/conf/adamliu.net.key"
  Protocols h2 h2c http/1.1

  ProxyPass "/"  "http://localhost:8000/"
  # 这句是让 3xx 之类的重定向地址可以被 Apache 捕获然后修改为 Apache 的对外URL
  ProxyPassReverse "/"  "http://localhost:8000/"

  # Error Documents
  ErrorDocument 503 /503.html

</VirtualHost>

本地电脑需要启用 SSH 远程端口转发:

# ServerAliveInterval 和 ServerAliveCountMax 是为了保持 session 常驻
ssh -o ServerAliveInterval=30 -o ServerAliveCountMax=5 -R localhost:8000:localhost:8000 ubuntu@18.123.123.123