原文

I did a bit more research and seem to have used better key words because I found my solution now. I wanted to share the solution with everyone, in case someone else may ever need it.

Create folder for configuring docker service through systemd

mkdir /etc/systemd/system/docker.service.d

Create service configuration file at /etc/systemd/system/docker.service.d/http-proxy.conf and put the following in the newly created file

[Service]
# NO_PROXY is optional and can be removed if not needed
# Change proxy_url to your proxy IP or FQDN and proxy_port to your proxy port
# For Proxy server which require username and password authentication, just add the proper username and password to the URL. (see example below)

# Example without authentication
Environment="HTTP_PROXY=http://proxy_url:proxy_port" "NO_PROXY=localhost,127.0.0.0/8"

# Example with authentication
Environment="HTTP_PROXY=http://username:password@proxy_url:proxy_port" "NO_PROXY=localhost,127.0.0.0/8"
Reload systemctl so that new settings are read

sudo systemctl daemon-reload

Verify that docker service Environment is properly set

sudo systemctl show docker --property Environment

Restart docker service so that it uses updated Environment settings

sudo systemctl restart docker

Now you can execute the docker-compose command on your machine without getting any connection refused error messages.

背景

某 spring 服务使用 nginx 反向代理(下文称为 nginx-a),对外提供服务,并且在 nginx 上配置了 gzip on

后来由于某些原因,需要在 nginx 前面再加一层 nginx 做反向代理(下文称为 nginx-b),并且没有开启 gzip,因为已经在 nginx-a 开启了 gzip,无需开启两次

问题

加上 nginx-b 之后发现,浏览器访 nginx-b 问的时 gzip 失效了,于是又把域名指向 nginx-a,发现 gzip 却是生效的。

结论

排查之后发现是由于 nginx 作为反向代理服务器(nginx-b),会给后端服务发送一个 header Via: nginx,并且 nginx 作为 upstream(nginx-a)回检查 header 中的 Via,默认配置下如果设置了这个 header,nginx 返回的内容就不做 gzip 压缩,所以访问 nginx-b 的时候 gzip 失效了。

解决办法

只需要在 nginx-b 的配置里加上 gzip_proxied: any,表示任何时候返回的内容都做 gzip,具体介绍见 nginx 官网文档

参考文献:

不要情绪化

有问题就陈述具体事项和带来的影响,而不是描述自己的感受和可能做出的应对策略

示例一

错误示例:你这么搞我没法干了(描述应对策略)

正确示例:你这么搞导致我做这件事情的成本太高了(陈述问题和带来的影响)

示例二

错误示例:各种流程太多了,让人觉得很烦(描述自身感受)

正确示例:各种流程太多了,导致做事情效率太低(陈述问题和带来的影响)

Dockerfile

在原来镜像的 Dockerfile 中添加如下定义

RUN pecl install xdebug-3.0.2
RUN docker-php-ext-enable xdebug

RUN { \
        echo 'zend_extension=xdebug.so'; \
        echo 'xdebug.mode=profile'; \
        echo 'xdebug.start_with_request=trigger'; \
        echo 'xdebug.output_dir=/tmp/xdebug'; \
    } > /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini;

重新打包镜像就能得到一个包含了 xdebug 扩展的 php 镜像了,如果不确定是否添加成功,可以在容器运行起来之后,执行docker exec -i container_name php -m | grep xdebug 检查 xdebug 扩展是否安装成功

配置解析

这里我们采用的是 xdebug3,配置与 xdebug2 不太一样,具体区别可以看这里,网上能搜到的大部分都是 xdebug2 的配置。

为了不影响其他请求的性能,也为了减少日志数量,我们采用 start_with_request=trigger,手动触发 xdebug profile

xdebug.mode=profile 表示使用 xdebug 的 profile 模式
xdebug.start_with_request=trigger 表示默认不开启 xdebug profile,需要通过 http 请求中,添加 > XDEBUG_PROFILE=1 参数来开启,GET/POST 均可
xdebug.output_dir 指定 xdebug 日志的存放位置,如果指定的文件夹不存在,是无法生成日志的

由于我们是在 docker 中使用 xdebug,xdebug.output_dir 对应的目录最好从宿主机挂载进去,方便查看

开始分析

构造一个请求,例如:http://127.0.0.1:8080/xdebug?XDEBUG_PROFILE=1

之后可以在上述配置的文件夹中找到文件名类似cachegrind.out.20的文件,就是我们要的 profile 文件了。

Mac 用户可以下载 qcachegrind 来查看 profile 文件 brew install qcachegrind

下载完之后打开文件就能看到图形化的分析结果了。

参考文献

问题描述

生产环境有一个场景是,a.com 会加载 b.com 下的一个静态资源 b.com/b.js,并且需要验证登陆才能拿到。通过如下代码获取

addScript('b.com/b.js')

function addScript(src, callback) {
  var script = document.createElement('script')
  script.src = src
  script.onload = script.onerror = function() {
    if (script.parentNode) {
      script.parentNode.removeChild(script)
    }
    if (callback) {
      callback()
      callback = null
    }
  }
  document.head.appendChild(script)
}

也就是我们常用的 jsonp 的形式

我们通常会手动创建 script 标签并设置 src 属性,来实现 jsonp 跨域请求

之前一直没问题,突然有一天有同事发现这个资源加载不到了,并且只是个别人会有这个问题。

查看之后发现,在出问题的浏览器中addScript('b.com/b.js')发出请求里没带 cookie,所以获取静态资源的请求直接 302 到了登陆页。经过一番查找,发现问题出在 cookie 的 SameSite 属性上。

在没有这个问题的浏览器中,通过手动设置 Chrome 的 chrome://flags/#same-site-by-default-cookies 为 Enabled 也能重现这个问题,至此问题确认。

image-20200804171245697.png

下面对 SameSite 做简单介绍。

SameSite 属性(引用自阮一峰博客

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。

它可以设置三个值。

  • Strict
  • Lax
  • None

2.1 Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

Set-Cookie: CookieName=CookieValue; SameSite=Strict;

这个规则过于严格,可能造成非常不好的用户体验。比如,当前网页有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。

2.2 Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

Set-Cookie: CookieName=CookieValue; SameSite=Lax;

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表。

请求类型示例正常情况Lax
链接<a href="..."></a>发送 Cookie发送 Cookie
预加载<link rel="prerender" href="..."/>发送 Cookie发送 Cookie
GET 表单<form method="GET" action="...">发送 Cookie发送 Cookie
POST 表单<form method="POST" action="...">发送 Cookie不发送
iframe<iframe src="..."></iframe>发送 Cookie不发送
AJAX$.get("...")发送 Cookie不发送
Image<img src="...">发送 Cookie不发送

设置了StrictLax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性。

2.3 None

Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。

下面的设置无效。

Set-Cookie: widget_session=abc123; SameSite=None

下面的设置有效。

Set-Cookie: widget_session=abc123; SameSite=None; Secure

解决方案

解决方案就是显式设置 cookie 的 SameSite 属性为 None。一定是显式设置,response 中的 Set-Cookie 头里一定要包含 SameSite: None 字样。

参考文献

备注:文中提到 jsonp 以及 scrip 标签的 src 属性,主要是为了能让搜这几个关键字的人有可能搜索到这篇文章