HomeAssistant 已经在内网工作很长一段时间,因为只能在内网访问,所以有时候会不太方便。因为家庭宽带一般不会提供公网 IP,所以需要一台可以在公网访问的服务器(VPS 和云服务器均可),一般的服务器都会分配一个公网固定 IP。
最终实现的效果是通过域名(不加端口号)访问部署在内网的 HomeAssistant。以下的教程服务器端在 CentOS 7 中实现,其它 Linux 发行版和 Windows 系统中大同小异,因为用到的工具 frp、Nginx 均提供了 Linux 和 Windows 的支持。在使用中, frp 本身已经是一个反向代理的软件,也可以不使用 Nginx 来做反向代理,但由于 frp 存在的时间不是特别长,且用途有些和谐,导致文档和功能不是很全面。Nginx 是一款高性能、多功能、使用范围极广的反向代理软件,加上本身服务器还使用了 Nginx 代理了网站,所以选择加上 Nginx 做反向代理,至少可以去掉端口号访问。
服务器端 frp 的安装
frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp, udp, http, https 协议。具体可参考:https://github.com/fatedier/frp/blob/master/README_zh.md 。
下载 frp
在 https://github.com/fatedier/frp/releases 页面下载最新的发行版,选择合适的版本,Linux 服务器选择带有 linux 版本,其中 386 代表 32 位的操作系统,amd64 代表 64 位的操作系统。例如 CentOS7 64 位的服务器应该选择 frp_0.16.0_linux_amd64.tar.gz
下载,因为 GitHub CDN 托管在亚马逊的云服务器上,且都在国外,经常会发生下载错误或者下载速度慢的情况,建议多尝试一下,或者更换其它网络下载。
解压安装 frp
解压出来的 frp 目录结构为:
└── frp_0.16.0_linux_amd64
├── frpc
├── frpc_full.ini
├── frpc.ini
├── frps
├── frps_full.ini
├── frps.ini
└── LICENSE
其中,需要使用到的是 frps
和 frps.ini
,frps.full.ini
是一个完整的服务器端配置示例,frpc
、 frpc.ini
、frpc.full.ini
是客户端的软件和配置文件,这里不需要使用。
因为 frps
是一个可执行的文件,所以不需要再进行安装。
配置服务器端
完整的 frps.ini
配置说明,可以参考 frps.full.ini
或者 https://github.com/fatedier/frp/blob/master/conf/frps_full.ini,这里我们不需要这么多的配置。
[common]
bind_port = 7000
vhost_http_port = 8123
privilege_token = 12345678
这里因为在 https 是使用 Nginx 来做的反向代理,所以在 frp 配置的时候仅仅使用它的 http 服务。如果需要直接使用 frp 的 https 服务,可以将 vhost_http_port
替换为 vhost_https_port
。其中 bind_port
是 frp 运行的端口,采用默认的配置就可以了,另外需要注意的是,这里配置的端口,需要设置为外网可以访问,如果外网无法访问这个端口,那么 frp 是无法进行端口映射的。这里的 privilege_token
是 frp v0.11.0 开始支持的特权模式,也可以不设置。
服务器的配置就完成了,接下来可以使用 ./frps -c ./frps.ini
命令启动 frp 服务器端。如果看到类似“Start frps success”字样,就代表启动成功了。
设置自启
Supervisor(http://supervisord.org/)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、重启一个或多个进程。用Supervisor管理的进程,当一个进程意外被杀死,supervisort监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写shell脚本来控制。
因为Supervisor是Python开发的,安装前先检查一下系统否安装了Python2.4以上版本。下面以CentOS7,Python2.7版本环境下,介绍Supervisor的安装与配置步聚:
Supervisor 安装
yum install epel-release
yum install -y supervisor
supervisor没有发布在标准的CentOS源在,需要安装epel源。这种方式安装的可能不是最新版本,但比较方便,安装完成之后,配置文件会自动帮你生成。
默认配置文件:/etc/supervisord.conf
,进程管理配置文件放到:/etc/supervisord.d/
目录下即可,默认日志文件:/tmp/supervisord.log
,可以查看进程的启动信息。
配置 frp 的 supervisor 设置
在 /etc/supervisord.d/
新建 frp.ini
,并输入下面的内容:
[program:frp]
command = /var/opt/frp_0.16.0_linux_amd64/frps -c /var/opt/frp_0.16.0_linux_amd64/frps.ini
autostart = true
command 里的路径修改位你的 frp 解压路径。
启动 supervisor
supervisord -c /etc/supervisord.conf
接着可以通过 supervisorctl
查看、启动、停止、重启 frp 服务。
客户端的 frp 安装
这里的客户端取决于你想在本地运行 frp 的设备,但是有一个前提条件是,这个设备要能访问到 HomeAssistant 服务。这个设备可以是你安装 HomeAssistant 的树莓派、家里的路由器(路由器需要支持)、甚至你也可以安装在你的电脑上。然后在 https://github.com/fatedier/frp/releases 页面下载设备对应的 frp 版本下来。
frp 的配置
配置 frpc.ini
:
[common]
server_addr = XXXX
server_port = 7000
privilege_token = 12345678
custom_domains = www.yourdomain.com
其中 server_addr
需要修改为服务器的 ip 地址,server_addr
需要修改为服务器 frp 运行的端口,如果前面设置了 privilege_token
,这里修改成和上面一样即可,custom_domains
修改为需要访问的域名,请先将这个域名解析到服务器上。
frp 启动
使用命令 ./frpc -c ./frpc.ini
启动 frp 服务,如果能看到类似 ProxyName [http], connect to server [XXX:7000] success!
的字样,则说明客户端 frp 已经和服务器端的 frp 连接好了。
如果此时在服务器上设置的 8123 端口也可以通过外网访问的话,那么这时访问 http://www.yourdomain.com:8123
已经能够看到 HomeAssistant了。
Nginx 设置
因为 HomeAssistant 采用了 WebSocket 技术,所以除了静态页面之外,其余的操作都需要使用 WebSocket。所以需要在 Nginx 代理转发的时候也配置好 WebSocket。
关于 HomeAssistant 的 WebSocket API 介绍,可以参考:https://home-assistant.io/developers/websocket_api/。在 https://home-assistant.io/developers/api/ 可以看到 HomeAssistant 的所有支持的 API 介绍。
下面是一个示例:
server {
listen 80;
server_name www.yourdomain.com;
location / {
rewrite ^ https://www.yourdomain.com$request_uri? permanent;
}
}
server {
listen 443;
server_name www.yourdomain.com;
ssl on;
ssl_certificate fullchain.pem;
ssl_certificate_key privkey.pem;
ssl_trusted_certificate chain.pem;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers AESGCM:ALL:!DH:!EXPORT:!RC4:+HIGH:!MEDIUM:!LOW:!aNULL:!eNULL;
ssl_prefer_server_ciphers on;
proxy_set_header X-Forwarded-For $remote_addr;
gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
#gzip_http_version 1.0;
gzip_comp_level 5;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
location /api/websocket {
proxy_pass http://127.0.0.1:8123/api/websocket;
proxy_read_timeout 60s;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'Upgrade';
access_log /var/log/nginx/www.yourdomain.com/access.log;
error_log /var/log/nginx/www.yourdomain.com/error.log;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_buffering off;
proxy_pass http://127.0.0.1:8123;
access_log /var/log/nginx/www.yourdomain.com/access.log;
error_log /var/log/nginx/www.yourdomain.com/error.log;
}
}
本文参考以下内容: