Apache HttpClient 使用代理服务器 Proxy 的一个小坑

今天在调试一个用 HttpClient 写的 Demo 的时候遇到了一个问题:

org.apache.http.impl.execchain.RetryExec execute
INFO: I/O exception (org.apache.http.conn.UnsupportedSchemeException) caught when processing request to {tls}->http://proxyserver:port->https://servername:443: http protocol is not supported

也就是在通过 HTTP Proxy 进行 HTTPS 连接的时候,HttpClient 报了一个不支持 HTTP 协议。查了一下发现问题在于我使用 HttpClient 的方法。

由于我在使用 HttpClient 的时候是手动创建的 Registry ,而在创建的时候没有注册 HTTP 协议,导致报了上面那个错误。之前错误的代码是:

        // Create a registry of custom connection socket factories for supported
        // protocol schemes.
        Registry socketFactoryRegistry = RegistryBuilder.create()
                .register("https", sslsf)
                .build();

        // Create a connection manager with custom configuration.
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

需要修改为:

        // Create a registry of custom connection socket factories for supported
        // protocol schemes.
        Registry socketFactoryRegistry = RegistryBuilder.create()
                .register("https", sslsf)
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .build();

        // Create a connection manager with custom configuration.
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);

加的这句保证了这个ConnectionSocketFactory 也可以处理 HTTP 协议,从而避免这个问题。

MySQL 隐式转化整理

前几天在微博上看到一篇文章:价值百万的 MySQL 的隐式类型转换感觉写的很不错,再加上自己之前也对MySQL的隐式转化这边并不是很清楚,所以就顺势整理了一下。希望对大家有所帮助。

当我们对不同类型的值进行比较的时候,为了使得这些数值「可比较」(也可以称为类型的兼容性),MySQL会做一些隐式转化(Implicit type conversion)。比如下面的例子:

mysql> SELECT 1+'1';
        -> 2
mysql> SELECT CONCAT(2,' test');
        -> '2 test'

很明显,上面的SQL语句的执行过程中就出现了隐式转化。并且从结果们可以判断出,第一条SQL中,将字符串的“1”转换为数字1,而在第二条的SQL中,将数字2转换为字符串“2”。

MySQL也提供了CAST()函数。我们可以使用它明确的把数值转换为字符串。当使用CONCA()函数的时候,也可能会出现隐式转化,因为它希望的参数为字符串形式,但是如果我们传递的不是字符串呢:

mysql> SELECT 38.8, CAST(38.8 AS CHAR);
        -> 38.8, '38.8'
mysql> SELECT 38.8, CONCAT(38.8);
        -> 38.8, '38.8'

隐式转化规则

官方文档中关于隐式转化的规则是如下描述的:

If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.

  • If both arguments in a comparison operation are strings, they are compared as strings.
  • If both arguments are integers, they are compared as integers.
  • Hexadecimal values are treated as binary strings if not compared to a number.
  • If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.
  • If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.
  • In all other cases, the arguments are compared as floating-point (real) numbers.

翻译为中文就是:

  • 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
  • 两个参数都是字符串,会按照字符串来比较,不做类型转换
  • 两个参数都是整数,按照整数来比较,不做类型转换
  • 十六进制的值和非数字做比较时,会被当做二进制串
  • 有一个参数是 TIMESTAMPDATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
  • 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
  • 所有其他情况下,两个参数都会被转换为浮点数再进行比较

注意点

安全问题:假如 password 类型为字符串,查询条件为 int 0 则会匹配上。

mysql> select * from test;
+----+-------+-----------+
| id | name  | password  |
+----+-------+-----------+
|  1 | test1 | password1 |
|  2 | test2 | password2 |
+----+-------+-----------+
2 rows in set (0.00 sec)

mysql> select * from test where name = 'test1' and password = 0;
+----+-------+-----------+
| id | name  | password  |
+----+-------+-----------+
|  1 | test1 | password1 |
+----+-------+-----------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+-----------------------------------------------+
| Level   | Code | Message                                       |
+---------+------+-----------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'password1' |
+---------+------+-----------------------------------------------+
1 row in set (0.00 sec)

相信上面的例子,一些机灵的同学可以发现其实上面的例子也可以做sql注入。

假设网站的登录那块做的比较挫,使用下面的方式:

SELECT * FROM users WHERE username = '$_POST["username"]' AND password = '$_POST["password"]'

如果username输入的是 a’ OR 1=’1 ,那么password随便输入,这样就生成了下面的查询:

SELECT * FROM users WHERE username = 'a' OR 1='1' AND password = 'anyvalue'

就有可能登录系统。其实如果攻击者看过了这篇文章,那么就可以利用隐式转化来进行登录了。如下:

mysql> select * from test;
+----+-------+-----------+
| id | name  | password  |
+----+-------+-----------+
|  1 | test1 | password1 |
|  2 | test2 | password2 |
|  3 | aaa   | aaaa      |
|  4 | 55aaa | 55aaaa    |
+----+-------+-----------+
4 rows in set (0.00 sec)

mysql> select * from test where name = 'a' + '55';
+----+-------+----------+
| id | name  | password |
+----+-------+----------+
|  4 | 55aaa | 55aaaa   |
+----+-------+----------+
1 row in set, 5 warnings (0.00 sec)

之所以出现上述的原因是因为:

mysql> select '55aaa' = 55;
+--------------+
| '55aaa' = 55 |
+--------------+
|            1 |
+--------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'a' + '55';
+------------+
| 'a' + '55' |
+------------+
|         55 |
+------------+
1 row in set, 1 warning (0.00 sec)

下面通过一些例子来复习一下上面的转换规则:

mysql> select 1+1;
+-----+
| 1+1 |
+-----+
|   2 |
+-----+
1 row in set (0.00 sec)

mysql> select 'aa' + 1;
+----------+
| 'aa' + 1 |
+----------+
|        1 |
+----------+
1 row in set, 1 warning (0.00 sec)

mysql> show warnings;
+---------+------+----------------------------------------+
| Level   | Code | Message                                |
+---------+------+----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'aa' |
+---------+------+----------------------------------------+
1 row in set (0.00 sec)

把字符串“aa”和1进行求和,得到1,因为“aa”和数字1的类型不同,MySQL官方文档告诉我们:

When an operator is used with operands of different types, type conversion occurs to make the operands compatible.

查看warnings可以看到隐式转化把字符串转为了double类型。但是因为字符串是非数字型的,所以就会被转换为0,因此最终计算的是0+1=1

上面的例子是类型不同,所以出现了隐式转化,那么如果我们使用相同类型的值进行运算呢?

mysql> select 'a' + 'b';
+-----------+
| 'a' + 'b' |
+-----------+
|         0 |
+-----------+
1 row in set, 2 warnings (0.00 sec)

mysql> show warnings;
+---------+------+---------------------------------------+
| Level   | Code | Message                               |
+---------+------+---------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b' |
+---------+------+---------------------------------------+
2 rows in set (0.00 sec)

是不是有点郁闷呢?

之所以出现这种情况,是因为+为算术操作符arithmetic operator 这样就可以解释为什么ab都转换为double了。因为转换之后其实就是:0+0=0了。

再看一个例子:

mysql> select 'a'+'b'='c';
+-------------+
| 'a'+'b'='c' |
+-------------+
|           1 |
+-------------+
1 row in set, 3 warnings (0.00 sec)

mysql> show warnings;
+---------+------+---------------------------------------+
| Level   | Code | Message                               |
+---------+------+---------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'a' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'b' |
| Warning | 1292 | Truncated incorrect DOUBLE value: 'c' |
+---------+------+---------------------------------------+
3 rows in set (0.00 sec)

现在就看也很好的理解上面的例子了吧。a+b=c结果为1,1在MySQL中可以理解为TRUE,因为’a’+’b’的结果为0,c也会隐式转化为0,因此比较其实是:0=0也就是true,也就是1.

第二个需要注意点就是防止多查询或者删除数据

mysql> select * from test;
+----+-------+-----------+
| id | name  | password  |
+----+-------+-----------+
|  1 | test1 | password1 |
|  2 | test2 | password2 |
|  3 | aaa   | aaaa      |
|  4 | 55aaa | 55aaaa    |
|  5 | 1212  | aaa       |
|  6 | 1212a | aaa       |
+----+-------+-----------+
6 rows in set (0.00 sec)

mysql> select * from test where name = 1212;
+----+-------+----------+
| id | name  | password |
+----+-------+----------+
|  5 | 1212  | aaa      |
|  6 | 1212a | aaa      |
+----+-------+----------+
2 rows in set, 5 warnings (0.00 sec)

mysql> select * from test where name = '1212';
+----+------+----------+
| id | name | password |
+----+------+----------+
|  5 | 1212 | aaa      |
+----+------+----------+
1 row in set (0.00 sec)

上面的例子本意是查询id为5的那一条记录,结果把id为6的那一条也查询出来了。我想说明什么情况呢?有时候我们的数据库表中的一些列是varchar类型,但是存储的值为‘1123’这种的纯数字的字符串值,一些同学写sql的时候又不习惯加引号。这样当进行select,update或者delete的时候就可能会多操作一些数据。所以应该加引号的地方别忘记了。

关于字符串转数字的一些说明

mysql> select 'a' = 0;
+---------+
| 'a' = 0 |
+---------+
|       1 |
+---------+
1 row in set, 1 warning (0.00 sec)

mysql> select '1a' = 1;
+----------+
| '1a' = 1 |
+----------+
|        1 |
+----------+
1 row in set, 1 warning (0.00 sec)

mysql> select '1a1b' = 1;
+------------+
| '1a1b' = 1 |
+------------+
|          1 |
+------------+
1 row in set, 1 warning (0.00 sec)

mysql> select '1a2b3' = 1;
+-------------+
| '1a2b3' = 1 |
+-------------+
|           1 |
+-------------+
1 row in set, 1 warning (0.00 sec)

mysql> select 'a1b2c3' = 0;
+--------------+
| 'a1b2c3' = 0 |
+--------------+
|            1 |
+--------------+
1 row in set, 1 warning (0.00 sec)

从上面的例子可以看出,当把字符串转为数字的时候,其实是从左边开始处理的。

  • 如果字符串的第一个字符就是非数字的字符,那么转换为数字就是0
  • 如果字符串以数字开头
  • 如果字符串中都是数字,那么转换为数字就是整个字符串对应的数字
  • 如果字符串中存在非数字,那么转换为的数字就是开头的那些数字对应的值

如果你有其他更好的例子,或者被隐式转化坑过的情况,欢迎分享。

参考资料

  • http://dev.mysql.com/doc/refman/5.7/en/cast-functions.html
  • https://blog.eood.cn/mysql_params
  • http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html

转自:http://www.cnblogs.com/rollenholt/p/5442825.html

退出当前 Bash Shell 并不保存历史的 5 种方法

某些时候我们希望在退出 Bash Shell 的时候不要保存执行命令的历史记录,那么可以用以下几种方法来实现:

先说两个不影响以前的历史记录的方法:

1. 修改 HISTFILE 变量

unset HISTFILE && exit

2. 直接 Kill 当前 Shell

kill -9 $$

下面三个方法会清除所有的记录:

3. 清除历史记录并退出

history -c && exit

4. 设置历史记录保存数量为 0 条并退出

HISTSIZE=0 && exit

5. 删除历史记录文件并修改 HISTFILE 变量

rm -f $HISTFILE && unset HISTFILE && exit

如果你想每次都自动执行这些命令,可以在将对应的指令添加到 ~/.bash_logout 文件中,或者使用 alisa 功能。

译自:http://www.if-not-true-then-false.com/2010/quit-bash-shell-without-saving-bash-history/

搭建需要身份认证的 Squid 代理

1. 安装 Squid

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install squid

或者

yum install squid

2. 配置 HTTP 代理
注意,在 Ubuntu 下配置文件的默认位置可能是 /etc/squid3/,下面均用 CentOS 的 /etc/squid/ 举例,请自行替换。
基础设置:
首先设置允许访问该代理的 IP 列表:

# /etc/squid/squid.conf
acl client src 12.34.56.78 #客户端1 IP 地址
acl client src 21.43.56.78 #客户端2 IP 地址
...
http_access allow client

注意这几行要加在配置文件原有的

http_access deny all

之前,不然不会生效。
然后重启 squid 服务,让配置生效。

service squid restart #CentOS
service squid3 restart #Ubuntu

注意!非常不建议使用 http_access allow all,你的代理一定会被别人扫到用来干坏事!

高级验证:
如果想使用用户名密码来做权限控制,则需要进行一些额外的配置:
首先,我们需要安装 htpasswd,这个工具集成在 apache httpd 的 tools 里面

apt-get install apache2-utils
yum install httpd-tools

安装完成之后执行 htpasswd,如果没有提示找不到命令,则说明安装成功。
接下来创建保存用户名密码的文件:

touch /etc/squid/squid_passwd
chown squid /etc/squid/squid_passwd

注意授权的时候要弄清楚 squid 运行时的用户名,一般是 squid 或者 proxy。
然后执行:

htpasswd /etc/squid/squid_passwd username
New password:
Re-type new password:
Adding password for user username

注意把 username 换成你想要的用户名。
如果想要继续添加用户,请多次执行这条命令。

接下来修改 /etc/squid/squid.conf 文件,在 http_access deny all 之前加上下面几句:

auth_param basic program /usr/lib/squid/ncsa_auth /etc/squid/squid_passwd
auth_param basic children 5
auth_param basic realm Squid proxy-caching web server
auth_param basic credentialsttl 2 hours
auth_param basic casesensitive off

acl ncsa_users proxy_auth REQUIRED
http_access allow ncsa_users
这里要注意,如果你的系统是 64 位的,那么 /usr/lib/squid/ncsa_auth 这个模块的地址应该是 /usr/lib64/squid/ncsa_auth。设置之前建议用这个命令来检查一下:
dpkg -L squid | grep ncsa_auth
rpm -ql squid | grep ncsa_auth

下面说说这几个选项的含义:
auth_param basic program /usr/lib/squid/ncsa_auth /etc/squid/squid_passwd : 指定密码文件和用来验证密码的程序
auth_param basic children 5 : 鉴权进程的数量
auth_param basic realm Squid proxy-caching web server : 用户输入用户名密码时看到的提示信息
auth_param basic credentialsttl 2 hours : 用户名和密码的缓存时间,也就是说同一个用户名多久会调用 ncsa_auth 一次。
auth_param basic casesensitive off : 用户名是否需要匹配大小写
acl ncsa_users proxy_auth REQUIRED : 所有成功鉴权的用户都归于 ncsa_users 组
http_access allow ncsa_users : 允许 ncsa_users 组的用户使用 Proxy

配置完成后重启 Squid 就可以啦!

3. 匿名
默认情况下,Squid 会添加很多和客户信息相关的 HTTP 头,如 X-Forwarded-For 这类。如果想要做到高度匿名,需要将这些头去掉。在 squid.conf 里面添加如下的配置:

forwarded_for off
request_header_access Allow allow all
request_header_access Authorization allow all
request_header_access WWW-Authenticate allow all
request_header_access Proxy-Authorization allow all
request_header_access Proxy-Authenticate allow all
request_header_access Cache-Control allow all
request_header_access Content-Encoding allow all
request_header_access Content-Length allow all
request_header_access Content-Type allow all
request_header_access Date allow all
request_header_access Expires allow all
request_header_access Host allow all
request_header_access If-Modified-Since allow all
request_header_access Last-Modified allow all
request_header_access Location allow all
request_header_access Pragma allow all
request_header_access Accept allow all
request_header_access Accept-Charset allow all
request_header_access Accept-Encoding allow all
request_header_access Accept-Language allow all
request_header_access Content-Language allow all
request_header_access Mime-Version allow all
request_header_access Retry-After allow all
request_header_access Title allow all
request_header_access Connection allow all
request_header_access Proxy-Connection allow all
request_header_access User-Agent allow all
request_header_access Cookie allow all
request_header_access All deny all

然后重启就可以啦!

默认情况下,Squid 会把主机相关的信息发送出去,并显示在错误页面。加上下面两句去掉这些信息:

# add this to /etc/squid/squid.conf
visible_hostname mybogusproxyhostname.local
# and while we are at it stop squid blabbing about it's version aswell
httpd_suppress_version_string on

最后说一个小坑,apache httpd 2.2 版本和 2.4 版本的 htpasswd 生成的密码文件格式是不一样的。只有 2.2 版本的能用。
如果按照教程设置成功以后发现用户名密码死活不对,文件权限也没有问题的话,那就要看一下是不是 htpasswd 的问题了。
直接输入 /usr/lib/squid/ncsa_auth /etc/squid/squid_passwd 然后输入 username passwd 可以手动运行一下 ncsa_auth 程序,看看是否是密码文件的问题。
如果提示 OK 那么说明没有问题,提示 ERR Wrong password 则说明要么是密码输错了,要么是密码文件的格式有问题。

PS:放出一个最简单的 PAC 文件,用来做代理的按需访问:

function FindProxyForURL(url, host) {
   if(ProxyDomain(url, host)) {
       return "PROXY 1.2.3.4:8080";
   }
   else {
       return "DIRECT";
   }
}

function ProxyDomain(url, host) {
    if(
        shExpMatch(host, "*.maoxian.de") ||
        shExpMatch(host, "*.baidu.com")
    ) {
        return true
    }
    return false;
}

PS:

编译安装的时候要注意一点,从 3.2 开始,编译使用的参数有了一点小变化,原先的:

--enable-auth="basic" --enable-baisc-auth-#helpers="NCSA"

变成了

--enable-auth  \
--enable-auth-basic=NCSA \

同时配置文件里的 /usr/lib/squid/ncsa_auth 模块可能会变成 /usr/bin/basic_ncsa_auth ,需要注意。

https://www.linode.com/docs/networking/squid/squid-http-proxy-ubuntu-12-04

常用 openssl 命令

The Most Common OpenSSL Commands

One of the most versatile SSL tools is OpenSSL which is an open source implementation of the SSL protocol. There are versions of OpenSSL for nearly every platform, including Windows, Linux, and Mac OS X. OpenSSL is commonly used to create the CSR and private key for many different platforms, including Apache. However, it also has hundreds of different functions that allow you to view the details of a CSR or certificate, compare an MD5 hash of the certificate and private key (to make sure they match), verify that a certificate is installed properly on any website, and convert the certificate to a different format. A compiled version of OpenSSL for Windows can be found here.

If you don’t want to bother with OpenSSL, you can do many of the same things with our SSL Certificate Tools. Below, we have listed the most common OpenSSL commands and their usage:

General OpenSSL Commands

These commands allow you to generate CSRs, Certificates, Private Keys and do other miscellaneous tasks.

  • Generate a new private key and Certificate Signing Request
    openssl req -out CSR.csr -new -newkey rsa:2048 -nodes -keyout privateKey.key
  • Generate a self-signed certificate (see How to Create and Install an Apache Self Signed Certificate for more info)
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt
  • Generate a certificate signing request (CSR) for an existing private key
    openssl req -out CSR.csr -key privateKey.key -new
  • Generate a certificate signing request based on an existing certificate
    openssl x509 -x509toreq -in certificate.crt -out CSR.csr -signkey privateKey.key
  • Remove a passphrase from a private key
    openssl rsa -in privateKey.pem -out newPrivateKey.pem

Checking Using OpenSSL

If you need to check the information within a Certificate, CSR or Private Key, use these commands. You can also check CSRs and check certificates using our online tools.

  • Check a Certificate Signing Request (CSR)
    openssl req -text -noout -verify -in CSR.csr
  • Check a private key
    openssl rsa -in privateKey.key -check
  • Check a certificate
    openssl x509 -in certificate.crt -text -noout
  • Check a PKCS#12 file (.pfx or .p12)
    openssl pkcs12 -info -in keyStore.p12

Debugging Using OpenSSL

If you are receiving an error that the private doesn’t match the certificate or that a certificate that you installed to a site is not trusted, try one of these commands. If you are trying to verify that an SSL certificate is installed correctly, be sure to check out the SSL Checker.

  • Check an MD5 hash of the public key to ensure that it matches with what is in a CSR or private key
    openssl x509 -noout -modulus -in certificate.crt | openssl md5
    openssl rsa -noout -modulus -in privateKey.key | openssl md5
    openssl req -noout -modulus -in CSR.csr | openssl md5
  • Check an SSL connection. All the certificates (including Intermediates) should be displayed
    openssl s_client -connect www.paypal.com:443

Converting Using OpenSSL

These commands allow you to convert certificates and keys to different formats to make them compatible with specific types of servers or software. For example, you can convert a normal PEM file that would work with Apache to a PFX (PKCS#12) file and use it with Tomcat or IIS. Use our SSL Converter to convert certificates without messing with OpenSSL.

  • Convert a DER file (.crt .cer .der) to PEM
    openssl x509 -inform der -in certificate.cer -out certificate.pem
  • Convert a PEM file to DER
    openssl x509 -outform der -in certificate.pem -out certificate.der
  • Convert a PKCS#12 file (.pfx .p12) containing a private key and certificates to PEM
    openssl pkcs12 -in keyStore.pfx -out keyStore.pem -nodes

    You can add -nocerts to only output the private key or add -nokeys to only output the certificates.

  • Convert a PEM certificate file and a private key to PKCS#12 (.pfx .p12)
    openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

Originally posted on Sun Jan 13, 2008

https://www.sslshopper.com/article-most-common-openssl-commands.html

dd 常用命令汇总

生成指定大小的文件

dd if=/dev/urandom of=sample.txt bs=1G count=1
dd if=/dev/urandom of=sample.txt bs=64M count=16

dd if=/dev/random of=sample.txt bs=1G count=1
dd if=/dev/random of=sample.txt bs=64M count=16

测试磁盘的读写速度

一般常用的能查到的测试磁盘读写速度的命令是下面几个:

dd bs=1M count=256 if=/dev/zero of=test
dd bs=1M count=256 if=/dev/zero of=test; sync
dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync
dd bs=1M count=256 if=/dev/zero of=test oflag=dsync

但是实际上,这几个的效果并不完全相同:

  1. dd bs=1M count=256 if=/dev/zero of=test
    The default behaviour of dd is to not “sync” (i.e. not ask the OS to completely write the data to disk before dd exiting). The above command will just commit your 256 MB of data into a RAM buffer (write cache) – this will be really fast and it will show you the hugely inflated benchmark result right away. However, the server in the background is still busy, continuing to write out data from the RAM cache to disk.
    dd 的默认行为是不做任何“同步”操作(也就是说,不要求操作系统在 dd 终止前完整的将数据写入磁盘。)上面那个命令只会给你分配 256M 的 RAM 缓存(写缓存)- 这个操作是非常快的,会告诉你非常夸张的测试觉果。但是实际上,服务器本身还在后台繁忙的进行写入操作,来将你刚刚写入 RAM 缓存的数据同步到磁盘。

    # dd bs=1M count=256 if=/dev/zero of=test
    256+0 records in
    256+0 records out
    268435456 bytes (268 MB) copied, 0.217855 s, 1.2 GB/s
  2. dd bs=1M count=256 if=/dev/zero of=test; sync
    Absolutely identical to the previous case, as anyone who understands how *nix shell works should surely know that adding a ; sync does not affect the operation of previous command in any way, because it is executed independently, after the first command completes. So your (inflated) MB/sec value is already printed on screen while that sync is only preparing to be executed.
    跟上一个命令完全相同。任何知道 *nix shell 如何运作的人都一定知道只是加一个 ; sync 并不会影响签名一条命令,因为这两条命令是完全分开执行的,添加的 sync 是在 dd 执行结束之后才会执行的。所以当你看到夸张的 MB/s 的值的时候,sync 命令才刚刚开始准备执行。

    # dd bs=1M count=256 if=/dev/zero of=test; sync
    256+0 records in
    256+0 records out
    268435456 bytes (268 MB) copied, 0.225096 s, 1.2 GB/s
    (Waiting for sync)
    #
  3. dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync
    This tells dd to require a complete “sync” once, right before it exits. So it commits the whole 256 MB of data, then tells the operating system: “OK, now ensure this is completely on disk”, only then measures the total time it took to do all that and calculates the benchmark result.
    这条指令告诉 dd 在退出之前必须要等待“同步”结束。所以该命令先是创建了 256M 的数据,然后告诉系统:“现在确认这些数据已经写到了磁盘上”,然后才开始停止计时并计算测试的结果。

    # dd bs=1M count=256 if=/dev/zero of=test conv=fdatasync
    256+0 records in
    256+0 records out
    268435456 bytes (268 MB) copied, 1.54606 s, 174 MB/s
  4. dd bs=1M count=256 if=/dev/zero of=test oflag=dsync
    Here dd will ask for completely synchronous output to disk, i.e. ensure that its write requests don’t even return until the submitted data is on disk. In the above example, this will mean sync’ing once per megabyte, or 256 times in total. It will be the slowest mode, as the write cache is basically unused at all in this case.
    这条命令 dd 会要求完全同步写入磁盘,也就是说,任何一个写入请求在数据完全保存到磁盘之前都不会返回。在上面这条命令里,这代表 dd 将会每 1MB 同步一次,或者说一共要同步256次。所以这是最慢模式,因为这种情况下基本上完全没有使用缓存。

    # dd bs=1M count=256 if=/dev/zero of=test oflag=dsync
    256+0 records in
    256+0 records out
    268435456 bytes (268 MB) copied, 2.40251 s, 112 MB/s

    当然有时候连续执行第三条和第四条命令,第四条并不比第三条慢。这可能是由于其他应用的 IO 占用导致的。

所以,一般情况下推荐使用第三条命令来进行测试,尽可能快的得到尽可能准确的结果。

原文地址:https://romanrm.net/dd-benchmark

Nginx 配置 403 Error Page

今天遇到一个很有意思的问题,就是给 Nginx 配置 403 时候的错误页面。因为这台 Nginx 针对访问 IP 做了一些限制,所以需要给无权限访问的用户展示一个友好的界面。

一开始我的设置是这么写的:

...
allow 10.0.0.0/24;
allow 192.168.0.0/24;
deny 1.2.3.4;
deny 2.3.4.5;

error_page 403 /403.html;
...

可是实际使用发现,无论如何 Nginx 展示的都是内置 Hard Code 进去的那个 404 页面,并不是我想让他展示的友好的界面。

在排除了各种文件权限之类的错误之后,我 Google 了一下发现,这个真正的原因在于前面配置的 deny 不仅 deny 掉了正常页面的访问,同时也将对 /404.html 页面的访问也 deny 掉了。

所以正确的配置是加上下面几句:

location = /403.html {
    root /path/to/403/page/;
    allow all;
    internal;
}

通过强制允许访问 /403.html 的方式来避免这个错误。下面加上 internal 的意思是这个页面不能被正常的访问到,只能因为 error_page 等内部原因而被访问到。详见这里

使用 Nginx 反代 Apache 安装 WordPress

最近由于原先博客主机极度不稳定,所以准备了很久,准备进行主机迁移。由于迁移前的环境和迁移后还是有较大的区别,整体架构也不太一样,所以在这里说说迁移过程中遇到的问题。

环境对比

原主机 新主机
操作系统 CentOS 6 CentOS 7
Web 服务器 Apache 2.2 Openresty 1.9.7 + Apache 2.4
PHP 版本 5.5 5.6
其他 SELinux

SELinux

很多人为了省事,在拿到主机的第一时间就直接禁用了 SELinux。不过在学习了一段时间之后,我发现其实 SELinux 是一个很好的保护机器的手段。

这里简单列举几个需要注意的 SELinux 的配置:

 

httpd_can_network_connect_db boolean 类型,控制 Apache 是否可以连接 DB
http_port_t port 类型,控制 Apache 可以监听的端口
mysqld_port_t port 类型,控制 mysql 可以监听的端口和 Apache 可以连接的 DB 端口

由于我是将 Apache 作为只解析后端 PHP 请求使用,所以需要修改 http_port_t 加入我需要的端口。添加方法类似于:

semanage port -a -t http_port_t  -p tcp 8090

另外由于我没有在本机安装 Mysql 而是使用的远程 Mysql 实例,并且开放的端口并不是标准的 3306,所以需要将端口号添加到 mysqld_port_t 中。

Apache

这玩意是主要的坑所在。下面来一一列举。

配置格式变更

Apache 2.2 和 2.4 的配置文件区别还是比较大的,加了很多新的参数,同时修改了很多配置的方法。最明显的是:

Options -Indexes

Allow from all
Deny from all

这三条配置已经完全被改掉了。如果在配置中出现第一种,会直接起不来。后面的 Allow Deny 的写法虽然不会有问题,但是已经不是官方推荐的了,建议改掉。

配置无效

在配置 Apache 2.4 的 Log Format 的时候,我发现了一个很蛋疼的问题,就是 CentOS yum 安装的版本(2.4.6)有些配置是使用不了的。如:

http://httpd.apache.org/docs/2.4/mod/mod_log_config.html

这个文档中的:

%{UNIT}T	The time taken to serve the request, in a time unit given by UNIT. Valid units are ms for milliseconds, us for microseconds, and s for seconds. Using s gives the same result as %T without any format; using us gives the same result as %D. Combining %T with a unit is available in 2.4.13 and later.

很多参数都有类似的标注(available in 2.4.13 and later),告诉你在旧版本中不能使用。如果不仔细看的话很容易忽略。所以配置之前一定要仔细阅读。

HTTPS

WordPress

由于我的博客是全站 HTTPS 的,因此在 WordPress 上我是有做一些强制 HTTPS 的措施。但是!由于 WordPress 默认的检测 HTTPS 的方法是这样的:

/**
 * Determine if SSL is used.
 *
 * @since 2.6.0
 *
 * @return bool True if SSL, false if not used.
 */
function is_ssl() {
        if ( isset($_SERVER['HTTPS']) ) {
                if ( 'on' == strtolower($_SERVER['HTTPS']) )
                        return true;
                if ( '1' == $_SERVER['HTTPS'] )
                        return true;
        } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
                return true;
        }
        return false;
}

而我的 HTTPS 是在 Nginx 层做的,所以导致这两个条件均不满足,因此会遇到重定向循环(Redirect Loop)的问题。解决方法有两种:

修改 wp-config.php 文件:

if ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && ( 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
    $_SERVER['HTTPS'] = 'on';
}

这里的修改需要配合修改 Nginx 的 Proxy 设置,增加下面这行:

proxy_set_header X-Request-Protocol $scheme; #http or https

修改 wp-includes/functions.php 文件(4025行左右):

找到上面说的那段代码,将其替换为:

/**
 * Determine if SSL is used.
 *
 * @since 2.6.0
 *
 * @return bool True if SSL, false if not used.
 */
function is_ssl() {
        if ( isset($_SERVER['HTTPS']) ) {
                if ( 'on' == strtolower($_SERVER['HTTPS']) )
                        return true;
                if ( '1' == $_SERVER['HTTPS'] )
                        return true;
        } elseif ( isset($_SERVER['SERVER_PORT']) && ( '443' == $_SERVER['SERVER_PORT'] ) ) {
                return true;
        } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && ( 'https' == $_SERVER['HTTP_X_FORWARDED_PROTO'] ) ) {
                return true;
        }
        return false;
}

同时这个修改方法也要配合修改 Nginx 的 Proxy 设置。

注意,虽然可以不做判断直接无脑 $_SERVER[‘HTTPS’] = ‘on’; 或者在那个 is_ssl() 方法中无脑返回 true,但是不建议这样修改。因为这样可能会导致安全上面的问题。

Apache .htaccess

由于我在某些目录中通过 .htaccess 的方式强制重定向了非 HTTPS 请求,因此在将 SSL 交给 Nginx 之后相关的跳转判断也要修改。原先的跳转逻辑是:

RewriteEngine on
RewriteCond   %{HTTPS} !=on
RewriteRule   ^(.*)  https://%{SERVER_NAME}/$1 [L,R]

由于 %{HTTPS} 这个变量判断的是 Apache 传进来的内部参数,我们无法控制,所以需要将这段修改为:

RewriteEngine on
RewriteCond %{HTTP:X-Request-Protocol} ^http$
RewriteRule   ^(.*)  https://%{SERVER_NAME}/$1 [L,R]

这里判断的就是 Nginx 传进来的 X-Request-Protocol 头了。不过这种写法并不能在单独使用 Apache 作为 Web 服务器的时候使用,需要注意。

客户端证书

目前只针对某目录或文件的客户端证书配置还没有测试完成,将在后面更新。

Compiling Nginx with HTTP/2 and ALPN

To have HTTP/2 fully supported in Nginx, you will need OpenSSL 1.0.2+ to have APLN enabled (What is APLN)

Unfortunately, nowadays most linux distributions are shipping with older version of OpenSSL. For example Ubuntu 14.04 is using openssl 1.0.1f

I can also use 3rd party repositories but I couldn’t find out something I think it is trusted

So it leaves me with only one option: compiling from source

Good thing is, it is pretty easy to compile nginx with a custom OpenSSL. You don’t even have to compile the OpenSSL and install into the system (which could break your system dependences)

All you have to do are:

  1. Download the latest stable OpenSSL from https://www.openssl.org/source/ and extract the tar
  2. Download the latest stable nginx from http://nginx.org/en/download.html and extract the tar
  3. Go into nginx source folder and
    ./configure --with-openssl=/path/to/openssl-1.0.2f --with-http_ssl_module --with-http_v2_module
    make && make install
    • Note: –with-openssl points to the openssl source folder instead of the installation folder
  4. Enable HTTP/2 in nginx configure file

That’s it

And here is how you can verify if your website is now supporting HTTP/2 and ALPN

echo |  /usr/local/ssl/bin/openssl s_client -alpn h2 -connect c11e.wodemo.com:443 | grep ALPN

Which will report

  • “ALPN protocol: h2”
  • or “No ALPN negotiated”