my blog my blog

Category: Linux
一个openai的ChatGPT反向代理实现

在奶牛看来,用api的同时保护自己不被暴露的安全性是最重要的。ChatGPT最近老火了,虽然奶牛还没用上4.0,一直在排队,但是也写点儿东西来弥补一下这个曾经没什么作品的遗憾吧。一直以来都没有发不过什么作品,这次写了个docker来实现这个反代。以OpenResty,通过lua来做安全验证防护,通过Nginx的proxy来反代。

使用方法:

docker pull nenew/openai-api
docker run -itd --name api-server -p 127.0.0.1:811:80 nenew/openai-api

你的openai反代地址: http://127.0.0.1:811
通过绑定到本地端口,可以阻止服务器被公众访问。
如果想让你的api反代服务器被公开,你也可以通过-e选项来绑定一个可以访问的IP或者设置一个密码来保证只有自己可以使用。

docker run -itd --name api-server -p 811:80 -e BIND_IP="123.123.123.123" nenew/openai-api
docker run -itd --name api-server -p 811:80 -e SECRET="Your secret" nenew/openai-api

注意:如果你设置了SECRET环境变量,你还需要设置你的app的请求头,增加X-Secret来完成验证:

X-Secret:Your secret

 测试:

访问 http://127.0.0.1/status 你可以看到你的请求头、请求体、设置的IP、设置的密码以及响应头的具体信息.
你也可以设置你的app的api到这个页面来进行测试或者使用Nginx的proxy_pass来设置转发。

proxy_pass http://1277.0.0.1/status

为什么你的反代不安全

其实写这个docker的原因很简单,看到很多不安全因素存在在你们的自建api里面。
首先你们没有去掉或者掩盖[“X-Real-IP”]和[“X-Forwarded-For”]。
反向代理的作用是保护目标服务器,但是我们其实想通过反代保护我们自己来保障我们的app可以正常的访问,所以完全没有必要把我们的X-Real-IP和X-Forwarded-For给目标服务器。我们建立反代其实目的是建立一个正向代理。这两个参数明显会暴露我们自己的真实IP,造成api的key多人使用的问题。
其次,很多人自建的api谁都可以用,比如像那些利用serverless实现的反代,你的访问IP地址每次都可能变化,那样子你的api key很可能就被判定为滥用【remote_addr一直是变化的】。我这个docker可以通过设置绑定IP和SECRET来阻止外界访问。
我认为最安全的方案应该是:
把这个docker放在你的独立服务器或者VPS上,你可以设置你的web app也在同一台服务器上,当然你也可以设置你的app来通过加密或者绑定IP的方式来达到隐藏自己的目的。

Ubuntu下安装Docker构建镜像教程

奶牛就做简单的记录了。在Ubuntu下使用

apt  install docker.io

这样子就完成安装了。然后我们需要登录https://hub.docker.com/settings/security设置Access Tokens

docker login -u nenew

输入生成的Tokens就可以登录了。之后我们通过Dockerfile构建镜像。

docker build -t nenew/python_run:v1.0 .
docker push nenew/python_run:v1.0
docker pull nenew/python_run:v1.0

这样子构建,推送,取回就都可以完成了。

关于minio的docker配置以及403错误解决方法

关于docker版本的minio安装,其实很容易,就按照官网的配置好就可以了,奶牛是群晖的nas,所以在nas的docker里面直接把minio给pull回来就好了,然后就是一些配置选项。

这里需要注意的是要指定KMS,要不S3会发生无法传输问题。

MINIO_KMS_MASTER_KEY
MINIO_ACCESS_KEY
MINIO_SECRET_KEY

主要就是配置这三个key,直接指定就好了,其中的access key和secret key对应的就是minio的登录用户名和密码,大概就这么理解就成了。

然后记得把路由器的端口绑定做好,绑定到9000端口。

然后就是同步使用aws s3的时候,可能会遇到403的bug,奶牛在使用ansible来做同步的时候也是看到了403错误,这里把403在aws cli里面显示的是

An error occurred (RequestTimeTooSkewed) when calling the ListBuckets operation: The difference between the request time and the server's time is too large.

就是说服务器间的时间差距太大了,一看自己的server,果然是时间不太准了,设置下

date -s "20201211 19:58:00"  #yyyymmdd hh:mm:ss

然后再执行就很容易了。

最后记录下aws cli的s3配置

aws configure
AWS Access Key ID [None]: Q3AM3UQ867SPQQA43P2F
AWS Secret Access Key [None]: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
Default region name [None]: us-east-1
Default output format [None]: ENTER

aws configure set default.s3.signature_version s3v4

aws --endpoint-url https://play.min.io:9000 s3 ls

aws --endpoint-url https://play.min.io:9000 s3 ls s3://mybucket

aws --endpoint-url https://play.min.io:9000 s3 mb s3://mybucket

aws --endpoint-url https://play.min.io:9000 s3 cp simplejson-3.3.0.tar.gz s3://mybucket

aws --endpoint-url https://play.min.io:9000 s3 rm s3://mybucket/argparse-1.2.1.tar.gz

aws --endpoint-url https://play.min.io:9000 s3 rb s3://mybucket

minio实现了s3的很多功能,但是权鉴什么的没仔细研究,分布式的同步方案要4个存储空间,也相对要求太高,不过简单易用,如果作为对象存储服务器,奶牛觉得还是有差距的,只是个小的替代品。

群晖NAS黑转白DS920+实战

奶牛家的黑裙NAS寿终就寝了,估计是电源挂了,也懒得修了,直接上了今年的新款DS920+,下面说说群晖黑转白的过程。

1.拆盘+换盘

把旧盘拆下来直接插到新机器上就ok了,数据都在。

2.开机联机升级系统

用群晖助手找到新的IP,然后联机上nas的5000端口,一路下一步。

3.重置安装

发现从ds5.x直接升级到ds6.x会有系统bug,直接用不了,索性有比较好的重置功能,不怕丢数据,也不怕raid信息丢失。现在已经在为raid0担心了,又入了一块儿银河企业盘做备份。

群晖后面有个reset口,用针插会听到嘀嘀的声音,听到声音后松开针,再在1秒钟内重新按住,会听到陆续的三声嘀嘀,机器进入重置模式,然后用下载的固件直接重置安装即可,默认的设置估计都不在了,帐号也不在了,bt下载的话transmission得重新安装,自己网上找教程就好了,还有就是transmission的数据记得转移,如果安装位置有所改变的话。

transmission的配置在/volume1/@appstore/transmission/var里面,新安装在哪里就从哪里复制一份备份进去,记得chown把用户名和组搞定。

MT的话建议把DHT关闭。剩下的就继续自己探索了。

群晖在控制面板开启ssh后可以用用户名登录,之后sudo su提升权限到root,这样子就不会出现权限尴尬的问题了,但是操作的东西记得把权限也改了。

使用Nginx Lua阻止对服务器IP的80/443端口直接访问

Lua作为Nginx服务器的脚本扩充非常好用,而且性能也很好。如果要使用Nginx Lua阻止对服务器IP的80/443端口直接访问,我们可以对nginx.conf进行如下设置

location / {
access_by_lua_block{
	if ( ngx.var.host ~= "www.nenew.net" ) then 
		ngx.exit(444)
	end
                    }
             }

其中的ngx.var.host代表目标域名,有几个变量可能会发生混淆:

ngx.var.server_name对应server_name:www.nenew.net
ngx.var.host对应host:test1.nenew.net
ngx.var.hostname对应hostname:nenew

这里我们就看啊,如果我们把IP绑定给其它域名,或者对方直接用hosts来进行dns解析,改变的是ngx.var.host,这个是访问者的目标域名,是变化的,所以这里block是针对的ngx.var.host,只要判定不相同即无法访问,我们直接给一个444状态,切断连接不响应。hostname是主机名,这里没有什么用,ngx.var.server_name变量是绑定域名,不能用绑定域名来判定,因为绑定域名会变,但是访问域名可能会造成改变。

Nginx配置noVNC的web页面教程

先说下noVNC的一键启动

./utils/launch.sh --listen localhost:6002--vnc localhost:6001

listen是监听本地端口,如果开放就不要用localhost而用0.0.0.0就好了 。vnc后面是vnc server的端口。

这个noVNC的启动项目没法直接用Nginx反代,因为涉及到websocket。官方的教程是这样做的

在nginx.conf的http字段中添加
upstream vnc_proxy {
    server 127.0.0.1:6080;
}
在nginx.conf的server字段中添加
location /websockify {
          proxy_http_version 1.1;
          proxy_pass http://vnc_proxy/;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection "upgrade";

          # VNC connection timeout
          proxy_read_timeout 61s;

          # Disable cache
          proxy_buffering off;
    }
location /vnc {
            index vnc.html;
            alias /home/nenew/noVNC/;
            try_files $uri $uri/ /vnc.html;
    }

其实很好理解,就是通过建立nginx和upstream的连接,然后把vnc的传输通过websocket完成。

这里nginx配置就算完成了,我们还需要一个websocket

./utils/websockify/run source_port target_addr:target_port

其中的源端口和目标地址:端口就正常填写即可,这里需要说的是目标端口和一键启动的端口没有关系,就是vnc的服务端口,websockify和launch俩程序没啥关系。

然后直接通过网站http://www.nenew.net/vnc访问即可。

Ubuntu Server安装KVM NAT虚拟机

安装kvm和libvert套装

apt install qemu qemu-kvm libvirt-bin  bridge-utils  virt-manager

下载好安装镜像并查看NAT网络

virsh net-list
查看会有一个default网络
ifconfig
会有一个私网的bridge网桥

开启IPV4转发

vi /etc/sysctl.conf
net.ipv4.ip_forward = 1
sysctl –p

安装KVM虚拟机

virt-install --name kvm --memory 4096 --vcpus=2 --boot hd,cdrom,menu=on --cdrom /home/nenew/ubuntu-18.04.3-desktop-amd64.iso --cdrom /home/nenew/virtio-win.iso --disk path=/home/nenew/kvm.img,format=qcow2,bus=virtio --network network=default --graphics vnc,listen=0.0.0.0,port=6001

这样配置一个可以在6001端口监听的VNC server,用VNC连接server并进行安装。

启动虚拟机

virsh list --all
查看所有虚拟机
virsh start kvm
启动虚拟机(安装完成后虚拟机可能关机)

奶牛现在码字感觉很费力,懒得多写了。

自建sit 6in4 Tunnel Broker IPv6隧道

从大概8年前就知道HE(Hurricane Electric)的Free IPv6 Tunnel Broker,但是印象中尝试一直是失败的,当然这次的尝试也是失败的,原因估计是电信运营商封了6in4这种数据包封装,不过还是把过程记录下吧。

先说说sit tunnel,其实和GRE tunnel啊IPIP tunnel是相似的,sit tunnel也是本地和远程对等的一种tunnel,也就是说服务器端和客户端几乎是相反的配置。这里的配置需要iproute2的支持。

客户端:

ip tunnel add user-ipv6 mode sit remote 服务器IPv4地址 local 本地IP(如果是内网则为内网地址) ttl 255
ip link set user-ipv6 up
ip addr add 服务器IPv6地址::2/80 dev user-ipv6
ip route add ::/0 dev user-ipv6

服务器端:

ip tunnel add server-ipv6 mode sit remote 客户公网IPv4地址 local 服务器IPv4地址 ttl 255
ip link set server-ipv6 up
ip addr add 服务器IPv6地址网关::1/80 dev server-ipv6
ip route add 服务器IPv6 Route地址::/80 dev server-ipv6
echo "net.ipv6.conf.all.forwarding = 1" >>/etc/sysctl.conf
sysctl -p

配置完成后需要添加邻居信息服务,防止IPv6地址无法被广播识别。

wget https://github.com/DanielAdolfsson/ndppd/archive/0.2.5.tar.gz
tar zxvf 0.2.5.tar.gz
cd ndppd-0.2.5
make
make install

vi /etc/ndppd.conf

route-ttl 30000
proxy eth0 {
router yes
timeout 500
ttl 30000
rule 你的:IPv6:子网:前缀::/前缀长度 {
static
}
}

ndppd -d

配置完成后客户端应该可以直接使用分配的IPv6地址了。这里需要注意的是/64的IPv6地址需要切分成/80的子网,然后进行广播设置。配置工具可以使用https://github.com/sskaje/6in4

但是使用的时候需要将sipcalc安装上,否则无法正确使用。使用方法:

cd 6in4-master/etc/
vi config.ini

; IPv6 network, must be ending with '::'
; * Required
IPV6_NETWORK=你的:IPv6:子网:前缀::

; IPV6 CIDR, must be a multiple of 8.
; * Required
; If your IPV6_CIDR is smaller than /64, assigned blocks are /64; if smaller than /48, /48 is used.
; If IPV6_CIDR is greater than /64, assigned blocks are /(IPV6_CIDR + 16 [ + 8 ]), e.g.: 64->80, 72->96, 80->96
IPV6_CIDR=前缀长度

; Bind tunnel to device INTERFACE
; * Required
INTERFACE=接口名

; Local IPv4
; * Optional
; This is useful if your $INTERFACE has more than one IP.
; If BIND_IP is not set, script reads first IPv4 ip from $INTERFACE
;BIND_IP=1.1.1.1 若接口上有多于1个IPv4地址 需要绑定

; MTU
; Default: 1480
;LINK_MTU=1480

:wq

cd 6in4-master
./bin/6to4 add 1 233.233.233.233

***************************** WARNING ********************************
IPV6_CIDR greater than 64 may cause your subnet not advertisable.
***************************** WARNING ********************************

Please set up tunnel on your machine with following parameters:
    Server IPv4 Address:        1.1.1.1
    Server IPv6 Address:        2001:0db8:0001:0001:1001::1/80
    Client IPv4 Address:        233.233.233.233
    Client IPv6 Address:        2001:0db8:0001:0001:1001::2/80
    Routed /80:                 2001:0db8:0001:0001:2001::/80

If you don't have a static IP, set your local to 0.0.0.0 and invoke update api to update your endpoint

 

OVH配置Ubuntu18.04 IPv6教程

传统方法(开机需要重新配置):

ip addr add IPv6-Address/64 dev ens3
ip -6 route add IPv6-Gateway dev ens3
ip -6 route add default via IPv6-Gateway dev ens3

Ubuntu 18.04 netplan配置方法:

vim /etc/netplan/ipv6.yaml 
network:
    version: 2
    ethernets:
        ens3:
            dhcp6 : no
            match :
                    name : ens3
            addresses :
                    - "IPv6-Address/64"
            gateway6 : "IPv6-Gateway"
netplan try
netplan apply

更多的配置文件可以参考/usr/share/doc/netplan.io下的配置。

奶牛也踩了个坑,之前系统有紧急bug,OVH给做了紧急的内核升级,所以这次要apt update && apt upgrade升级下内核之后才可以,避免出现路由错误问题。

Ubuntu18.04编译安装MySQL 5.7.26

下载带有boost版本的MySQL 5.7.26

wget https://downloads.mysql.com/archives/get/file/mysql-boost-5.7.26.tar.gz

安装依赖

apt install cmake
apt install libncurses5 libncurses5-dev 
apt install bision

解压

tar xzf mysql-boost-5.7.26.tar.gz 
cd mysql-5.7.26/

编译安装

说明一下,其实这个boost并不需要单独安装,只需要把包内的boost路径加载到configure就可以了。

cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DSYSCONFDIR=/etc -DWITH_MYISAM_STORAGE_ENGINE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DWITH_PARTITION_STORAGE_ENGINE=1 -DWITH_FEDERATED_STORAGE_ENGINE=1 -DEXTRA_CHARSETS=all -DDEFAULT_CHARSET=utf8mb4 -DDEFAULT_COLLATION=utf8mb4_general_ci -DWITH_EMBEDDED_SERVER=1 -DENABLED_LOCAL_INFILE=1 -DWITH_BOOST=./boost
make
make install

完成后的工作

 

useradd -M -s /sbin/nologin mysql
groupadd mysql
chown mysql:mysql /usr/local/mysql/var/
chgrp -R mysql /usr/local/mysql/
mkdir -p /usr/local/mysql/var
cp support-files/mysql.server /etc/init.d/mysql
chmod +x /etc/init.d/mysql 
cat > /etc/ld.so.conf.d/mysql.conf<<EOF
    /usr/local/mysql/lib
    /usr/local/lib
EOF
ldconfig
/usr/local/mysql/bin/mysqld --initialize-insecure --basedir=/usr/local/mysql --datadir=/usr/local/mysql/var/ --user=mysql --explicit_defaults_for_timestamp=true
初始化数据库
vim /etc/my.cnf
配置数据库
service mysql start
/usr/local/mysql/bin/mysqladmin -u root password password
设置管理员密码

附上LNMP的my.cnf

[client]
#password	= your_password
port		= 3306
socket		= /tmp/mysql.sock

[mysqld]
port		= 3306
socket		= /tmp/mysql.sock
datadir = /usr/local/mysql/var
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
thread_cache_size = 8
query_cache_size = 8M
tmp_table_size = 16M

#skip-networking
max_connections = 500
max_connect_errors = 100
open_files_limit = 65535

log-bin=mysql-bin
binlog_format=mixed
server-id	= 1
expire_logs_days = 10

default_storage_engine = InnoDB
innodb_file_per_table = 1
innodb_data_home_dir = /usr/local/mysql/var
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = /usr/local/mysql/var
innodb_buffer_pool_size = 16M
#innodb_additional_mem_pool_size = 2M
innodb_log_file_size = 5M
innodb_log_buffer_size = 8M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50

[mysqldump]
quick
max_allowed_packet = 16M

[mysql]
no-auto-rehash

[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M

[mysqlhotcopy]
interactive-timeout