pc 移动端app必须都支持
为了省阿里云服务器流量,服务器需要的三个访问路径
1、需要内网可以通过ip+port直接访问
2、外网可以通过ddns访问,因为443和80端口都被封,只能换端口,但依然需要ssl
3、为了防止ddns挂掉,通过阿里云配置内网穿透。阿里云由443端口,所以同样需要ssl。
内网访问,直接访问nextcloud
ddns访问。单个nginx。由于不止一个web服务,所以内网搭建nginx,反向代理到nextcloud。内网nginx负责域名解析,ssl配置。
阿里云访问。nginx+nps。本身备案需要开一个web用于定期检查。又需要内网穿透和域名解析。所以配置nginx,www直接指向阿里云本身(检查要求,www指向阿里云服务器),其他二级域名直接把流量通过内网穿透发送到内网nginx上,内网nginx做域名解析。穿透用的nps
阿里云nginx转发到内网nginx,内网nginx做域名解析配置ssl。通过阿里云访问正常。
ddns直接访问内网nginx,网页登录提示
refused to send form data to xxx.xx.xx because it violates the following content security policy directive: "form-action 'self'".
win 客户端提示从https访问,尽管登录 url 以 https 开头,但轮询 url 中没有,你将无法登录,因为这是一个安全问题
手机app登录提示access forbidden invalid request,还有提示严格模式,只允许https访问,即使设置了http。
这里直接贴解决办法,具体过程和排查在后面。
nextcloud关键配置
'overwritecondaddr' => '^192\\.168\\.1\\.141$',#nginx的ip
#nextcloud拿到的ip与overwritecondaddr做正则,通过,则会用使用overwriteprotocol定义的值替换http。
'overwriteprotocol' => 'https',
内网nginx配置
location / {
proxy_set_header X-Real-IP '192.168.1.144';#设置为nginx所在的ip地址
#X-Forwarded-For:nextcloud通过此获取客户端实际ip,用于防止密码暴力破解等问题。
#如果ddns访问或者通过穿透访问时,一个客户端密码试错导致所有客户端都提示ip被锁定,
#可以看是不是这个配错了,导致nextcloud拿到的所有客户端ip都是nginx的ip或者阿里云的ip
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto 'https';#从nginx访问的,只允许通过https,所以写死,不用$scheme了;
proxy_set_header Host $http_host;#因为是带端口的访问,所以这里必须用$http_host
proxy_pass http://192.168.1.145:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;#升级http为wss,加上这两条以支持wss
proxy_set_header Connection "upgrade";
}
关键在proxy_set_header X-Real-IP '192.168.1.144'。X-Real-IP值需要能被nextcloud的config.php中overwritecondaddr的正则匹配成功,成功后会为域名加上https,否则认为不是通过反向代理访问的,不会加https。
根据网上其他nginx配置,都写着X-Real-IP 需要配置为$remote_addr,翻源码后才发现nextcloud不是能这么设置。
注!!!过程中的一些配置仅是提供排查思路,不是最终解决办法
首先遇到的问题是登录提示refused to send form data…,搜了一圈都说是nextcloud的 html/config/config.php配置文件中加入
'overwriteprotocol' => 'https',
'overwritecondaddr' => '^192\\.168\\.1\\.144$',
然而加入并没有用,依然提示。
然后搜了说是ContentSecurityPolicy(CSP)的配置,找到了配置文件,在html\lib\public\AppFramework\Http\ContentSecurityPolicy.php里面
protected $allowedFormActionDomains = [
'\'self\'',
'http://pan.example.com:8989',//加入允许的地址,8989是我自己通过外网访问的端口
];
已经配好了ssl,访问用的是https,但提示拒绝发送,说明这个表单发送的地址不是https://pan.example.com:8989,那就试试http(实际试过各种,不带http的,不带端口号的,文档也没说清楚,我也不懂,又搜到不带http/https的配置,简直无语)
设置后,web登录成功。然后尝试win 客户端登录。登录时客户端提示
尽管登录 url 以 https 开头,但轮询 url 中没有,你将无法登录,因为这是一个安全问题
文字和句子我都看懂了,但是轮询哪里的url啊,不知道怎么改,继续搜
搜了一圈,解决办法依然和refused to send form data… 的一样,我…
(╯‵□′)╯︵┻━┻
但是搜到一个关于nextcloud介绍的,偶然看到一个管理员设置里的手机与电脑切页,下面有个服务器地址
看到帖子里是https,我这是http,一瞬间似乎明白了问题。refused to send form data… 因为地址是https,但表单提交的地址是http,所以在CSP中加入http域名信任能成功。所以客户端登录提示尽管用了https,但url没有,说明什么,说明nextcloud内部生成的连接都是http的。
我w(゚Д゚)w
我配了'overwriteprotocol','overwritecondaddr'这两个了啊,怎么内部还是http的。
那就继续试,首先注释掉overwritecondaddr。登录管理员,看服务器地址,是https了。再试试客户端,都登录成功。最后试下内网ip访问,完蛋,http://192.168.1.145:8080被重定向到https://192.168.1.145:8080,然后浏览器提示重定向次数过多,无法打开web。内网无法通过ip访问,难道在家还要通过宽带那上传小水管绕一圈?同步的时候不得把上行吃满。
不行,再查。然而到此就毫无头绪了,本来nginx+ssl+非443端口就没几个人搞。那只能,查源码!!
╥﹏╥…
直接利用github的搜索功能,查找overwritecondaddr和overwriteprotocol是如何生效的。然后在html\lib\private\AppFramework\Http\Request.php里找到了如下代码
/**
* Check overwrite condition
* @param string $type
* @return bool
*/
private function isOverwriteCondition(string $type = ''): bool {
$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/';
$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
return $regex === '//' || preg_match($regex, $remoteAddr) === 1
|| $type !== 'protocol';
}
/**
* Returns the server protocol. It respects one or more reverse proxies servers
* and load balancers
* @return string Server protocol (http or https)
*/
public function getServerProtocol(): string {
if ($this->config->getSystemValue('overwriteprotocol') !== ''
&& $this->isOverwriteCondition('protocol')) {
return $this->config->getSystemValue('overwriteprotocol');
}
if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
$proto = strtolower(trim($parts[0]));
} else {
$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
}
// Verify that the protocol is always HTTP or HTTPS
// default to http if an invalid value is provided
return $proto === 'https' ? 'https' : 'http';
}
if (isset($this->server['HTTPS'])
&& $this->server['HTTPS'] !== null
&& $this->server['HTTPS'] !== 'off'
&& $this->server['HTTPS'] !== '') {
return 'https';
}
return 'http';
}
第7行,isOverwriteCondition()方法中,获取overwritecondaddr值,拼接正则表达式,第8行获取REMOTE_ADDR,拿去匹配正则表达式。
第20行,getServerProtocol()方法,调用isOverwriteCondition()判断是否需要覆盖协议,而覆盖的就是overwriteprotocol中的值,也就是设置的https。
所以只要让isOverwriteCondition()返回true,内部就会使用https代替http,那么问题就出在了正则表达式的匹配上。那就打印$remoteAddr看看,在第8行后面插入print 'remote addr 测试'.$remoteAddr;,重启nextcloud。刷新web页面,直接出现了错误提示,在页面顶部就出现了打印的内容
这个ip不就是我客户端的ip吗,说好的overwriteprotocol是去匹配代理服务器ip的,为啥代理服务器ip地址是客户端ip。看来问题出在nginx上,于是修改nginx配置。
上配置
http{
map $host $path_pool{#二级域名和ip映射池,就不用每个二级域名配一个server了
doc.example.com http://192.168.1.146;
pan.example.com http://192.168.1.145:8080;
panOut.example.com http://192.168.1.145:8080;
}
server {
#用于阿里云穿透,穿透软件通过其他端口接收请求后,在内网执行请求,所以是在内网访问443端口
listen 443 ssl;
listen 8989 ssl;#用于ddns访问的端口。
server_name *.example.com;#直接匹配这个域名的所有二级域名
# 动态证书路径,这里使用了$ssl_server_name,就不用为每个域名单独配置证书,证书名字直接改成pan.example.com.pem,放到目录下。
# 每次请求会拼接出证书路径,缺点是对性能会有损失,可以看nginx文档
ssl_certificate /home/nginx/ssl/$ssl_server_name.pem;
ssl_certificate_key /home/nginx/ssl/$ssl_server_name.key; # 绝对路径,同上
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置,不懂,抄的
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置。不懂,抄的
ssl_prefer_server_ciphers on;#不懂,抄的
#设定非80端口访问时,强制到https。也就是访问http://pan.example.com:8989时,会返回浏览器497错误,然后重定向到https
error_page 497 https://$http_host$request_uri;
location / {
proxy_set_header REMOTE_ADDR $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto 'https';#从nginx访问的,只允许通过https,所以写死,不用$scheme了;
proxy_set_header Host $http_host;#注意使用$http_host
proxy_pass $path_pool;#$path_pool定义在上面了
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;#升级http为wss,加上这两条以支持wss
proxy_set_header Connection "upgrade";#
}
#nextcloud文档说要配这个,因为用域名+端口方式,所以$host记得都改成$http_host,$host是不带端口的
location = /.well-known/carddav {
return 301 $scheme://$http_host/remote.php/dav;
}
#nextcloud文档说要配这个
location = /.well-known/caldav {
return 301 $scheme://$http_host/remote.php/dav;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
配置有点长,顺便贴一下其他我觉得有用的部分。还记得,nextcloud里取REMOTE_ADDR去匹配代理服务器的正则吗,所以nginx配置里首先尝试修改 proxy_set_header remote-addr $remote_addr;,直接$remote_addr替换为nginx的ip。看了开头的结局方案就知道,这么改没用。
o(≧口≦)o
nextcloud的代码怎么骗人呢。于是,把这里所有$remote_addr都替换成nginx的ip,成功了,nextcloud里显示服务器地址是https,内网也可以用ip+port打开,win客户端,手机app都可以了。那么就再一个个改回$remote_addr,毕竟nextcloud的防止暴力破解还是需要的。最终定位到X-Real-IP,所以只用把X-Real-IP设置为nginx 的ip就可以了。
但是这个server并不只有nextcloud用,可能其他web服务又需要remote-addr,那就复制一个server,单独匹配nextcloud的域名吧,单独修改。
到此结束,于是写了这篇文档给这个坑绑条绳子,有人踩进来的话还能爬出去
ಥ_ಥ
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
我正在尝试将以下SQL查询转换为ActiveRecord,它正在融化我的大脑。deletefromtablewhereid有什么想法吗?我想做的是限制表中的行数。所以,我想删除少于最近10个条目的所有内容。编辑:通过结合以下几个答案找到了解决方案。Temperature.where('id这给我留下了最新的10个条目。 最佳答案 从您的SQL来看,您似乎想要从表中删除前10条记录。我相信到目前为止的大多数答案都会如此。这里有两个额外的选择:基于MurifoX的版本:Table.where(:id=>Table.order(:id).
我目前正在用Ruby编写一个项目,它使用ActiveRecordgem进行数据库交互,我正在尝试使用ActiveRecord::Base.logger记录所有数据库事件具有以下代码的属性ActiveRecord::Base.logger=Logger.new(File.open('logs/database.log','a'))这适用于迁移等(出于某种原因似乎需要启用日志记录,因为它在禁用时会出现NilClass错误)但是当我尝试运行包含调用ActiveRecord对象的线程守护程序的项目时脚本失败并出现以下错误/System/Library/Frameworks/Ruby.frame
我有一个应用需要发送用户事件邀请。当用户邀请friend(用户)参加事件时,如果尚不存在将用户连接到该事件的新记录,则会创建该记录。我的模型由用户、事件和events_user组成。classEventdefinvite(user_id,*args)user_id.eachdo|u|e=EventsUser.find_or_create_by_event_id_and_user_id(self.id,u)e.save!endendend用法Event.first.invite([1,2,3])我不认为以上是完成我的任务的最有效方法。我设想了一种方法,例如Model.find_or_cr
A/ctohttp://wiki.nginx.org/CoreModule#usermaster进程曾经以root用户运行,是否可以以不同的用户运行nginxmaster进程? 最佳答案 只需以非root身份运行init脚本(即/etc/init.d/nginxstart),就可以用不同的用户运行nginxmaster进程。如果这真的是你想要做的,你将需要确保日志和pid目录(通常是/var/log/nginx&/var/run/nginx.pid)对该用户是可写的,并且您所有的listen调用都是针对大于1024的端口(因为绑定(
在许多ruby类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..
如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si
例如,假设我有一个名为Products的模型,并且在ProductsController中,我有以下代码用于product_listView以显示已排序的产品。@products=Product.order(params[:order_by])让我们想象一下,在product_listView中,用户可以使用下拉菜单按价格、评级、重量等进行排序。数据库中的产品不会经常更改。我很难理解的是,每次用户选择新的order_by过滤器时,rails是否必须查询,或者rails是否能够以某种方式缓存事件记录以在服务器端重新排序?有没有一种方法可以编写它,以便在用户排序时rails不会重新查询结果