分类 Linux运维 下的文章

通过ssh登录时报错:

Host key verification failed.

最开始以为是服务端的问题,但是排查发现不管登录哪台设备都是报这个错,因此肯定不是服务端的问题了。

打印ssh连接日志发现有如下错误信息:

debug1: checking without port identifier
debug1: read_passphrase: can't open /dev/tty: Permission denied
Host key verification failed.

以前从来没有遇到这个问题,查看/dev/tty的权限发现是600,而其他的设备正常是666

于是修改/dev/tty的权限,再次登录就好了:

sudo chmod 666 /dev/tty

准备好证书和私钥文件,重命名为:rui.certrui.key

开启SSH:

登陆后台,把证书和私钥拷贝到/etc/vmware/ssl/目录覆盖,然后使用services.sh restart重启服务。

一、概述

之前安装好了ModSecurity作为nginx的WAF,但是后续的使用中发现OWASP-CRS规则过于苛刻,很多正常操作都会被阻挡,甚至打开一个正常的页面都会被拦截。每次都要手动排除规则十分麻烦,可以考虑使用第三方规则库:comodo规则库地址(访问需要翻墙)

comodo规则库是免费使用的,支持apache/httpd/nginx等多种web应用。使用前要先注册账号,支付0元之后将会得到为期一年的授权,此时就可以下载最新的规则库使用了,使用方式和OSASP-CRS一样,只需把规则文件加到ModSecurity的配置文件中即可。同时,官方还提供了非常简易的操作面板和详细的操作文档。

二、使用方式

如果没有安装ModSecurity先参考nginx安装modsecurity实现waf功能进行安装,ModSecurity的配置文件放在/etc/nginx/目录下。

当前最新版的comodo规则库版本为1.208,解压规则库,放到/etc/nginx/waf/comodo目录:

tar cwaf_rules_nginx_3-1.208.tgz -C /etc/nginx/waf/comodo

修改/etc/nginx/modsecurity.conf文件,设置新的规则文件位置:

Include /etc/nginx/waf/comodo/rules.conf.main

重启nginx服务,然后测试规则是否生效:

[ma@ubuntu comodo]$ curl -I http://localhost
HTTP/1.1 200 OK
Server: Tomcat/1.0.2
Date: Sun, 02 Jun 2019 01:38:52 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 14 Dec 2018 14:04:25 GMT
Connection: keep-alive
ETag: "5c13b869-264"
Accept-Ranges: bytes

[ma@ubuntu comodo]$ curl -I 'http://localhost/?search=<scritp>alert('xss');</script>'
HTTP/1.1 403 Forbidden
Server: Tomcat/1.0.2
Date: Sun, 02 Jun 2019 01:39:17 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

第二个xss攻击的被拦截了,规则生效。

一、ModSecurity和OWASP

ModSecurity是一个免费、开源的Apache模块,可以充当Web应用防火墙(WAF)。ModSecurity从3.0开始支持nginx,配合nginx的灵活和高效,可以打造成生产级的WAF,是保护和审核web安全的利器。

ModSecurity的主要功能在于,提供可靠的保护,远离各种网上威胁。它不是将安全重心偏离应用程序,而是增添了全局级别的功能。想对它进行配置,只需为客户机与服务器之间通信的每一个部分用条件和操作来指定规则,这些部分包括请求头、请求主体、响应头和响应主体。因而,ModSecurity可以预防针对Web服务器、PHP、Perl和ASP等解释器以及Web应用程序发动的攻击。

OWASP是一个安全社区,开发和维护着一套免费的应用程序保护规则,这就是所谓OWASP的ModSecurity的核心规则集(即CRS)。我们可以通过ModSecurity手工创建安全过滤器、定义攻击并实现主动的安全输入验证。

二、nginx中添加ModSecurity模块

2.1 查看当前nginx编译选项

如果设备已经安装了nginx,先记住当前的nginx编译选项。使用nginx -V命令可以查看编译选项:

[art@centos7 ~]$ nginx -V
nginx version: Tomcat/1.0.2
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/usr/local/nginx-1.12.2 --with-http_stub_status_module --with-http_ssl_module

最后一行就是nginx安装时的编译选项,先记住。

2.2 下载ModSecurity代码

拉取ModSecurity主代码,当前版本是3.x。ModSecurity在3.0以后已经不止是一个模块了,主代码库编译出来的是一个公共库,可以被第三方代码直接引用,现如今的ModSecurity-nginx就是通过这个lib库来完成的。

因此,整个modsecurity功能目前一共涉及到三个东西:

  1. ModSecurity的lib库
  2. ModSecurity的nginx模块
  3. owasp规则

克隆ModSecurity代码:

git clone https://github.com/SpiderLabs/ModSecurity.git

克隆ModSecurity-nginx代码:

git clone https://github.com/SpiderLabs/ModSecurity-nginx.git

克隆owasp-modsecurity-crs代码:

git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git

三个代码都克隆完成后,应该存在以下三个目录:

maqian@Inspiron:/usr/src/software$
total 20
drwxrwxr-x  5 root   root   4096 6月   1 19:48 ./
drwxr-xr-x  9 root   root   4096 6月   1 19:41 ../
drwxrwxr-x 13 maqian maqian 4096 6月   1 19:44 ModSecurity/
drwxrwxr-x  5 maqian maqian 4096 6月   1 19:47 ModSecurity-nginx/
drwxrwxr-x  7 maqian maqian 4096 6月   1 19:48 owasp-modsecurity-crs/

进入到ModSecurity目录,初始化子模块,这一步相当重要,否则后面会出错

cd ModSecurity
git submodule init
git submodule update

2.3 集成ModSecurity到nginx

2.3.1 下载依赖项

依赖项注意区分系统环境,除此之外后面的流程都不再依赖系统环境。

CentOS环境:

sudo yum install autoconf automake libtool gcc gcc-c++ make
sudo yum install yum install prce pcre-devel openssl openssl-devel

Ubuntu系统:

sudo apt-get install autoconf automake libtool gcc g++ make
sudo apt-get install openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev

2.3.2 编译ModSecurity

进入ModSecurity目录:

./build.sh
./configure
make && sudo make install

因为后面的modsecurity-nginxnginx编译都依赖modsecurity的lib库,为了方便起见,直接把编译后的库文件放到默认目录,不手动指定安装目录。

2.3.3 nginx编译安装

如果没有安装nginx,先下载好nginx代码,nginx的编译安装参考:CentOS源码编译安装NGINX。如果已经安装了nginx,记录nginx当前的编译选项,参考步骤2.1

nginx是否安装不重要,不同的是如果没有安装就要先下载源代码,如果安装了就记录下编译选项。

假设安装时候的编译选项为:

--user=www --group=www --prefix=/usr/local/nginx-1.12.2 \
    --with-http_stub_status_module --with-http_ssl_module

进入nginx目录:

./configure --user=www --group=www \
    --prefix=/usr/local/nginx-1.12.2 \
    --with-http_stub_status_module --with-http_ssl_module \
    --add-module=../ModSecurity-nginx/
make && sudo make install

安装好后nginx的配置参考上面的文档即可。

2.3.4 配置OWASP Core Rule Set

owasp-modsecurity-crs开始已经下载到本地,首先拷贝一份到nginx配置目录,假设安装到/etc/nginx/owasp目录下:

cp owasp-modsecurity-crs /etc/nginx/owasp/ -rf
mv /etc/nginx/owasp/crs-setup.conf.example /etc/nginx/owasp/crs-setup.conf

配置ModSecurity:

cp ModSecurity/modsecurity.conf-recommended /etc/nginx/modsecurity.conf
cp ModSecurity/unicode.mapping /etc/nginx/

modsecurity.conf中添加crs-setup.conf的配置:

SecRuleEngine On
Include /etc/nginx/owasp/crs-setup.conf
Include /etc/nginx/owasp/rules/*.conf

在nginx的配置中开启modsecurity模块,可以放在server段或者location段内:

modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity.conf;

测试配置文件是否正确:

nginx -t -c /etc/nginx/nginx.conf

可能会报错:

2019/06/01 20:56:09 [emerg] 29985#0: "modsecurity_rules_file" directive Rules error. File: /etc/nginx/owasp/rules/REQUEST-910-IP-REPUTATION.conf. Line: 71. Column: 22. This version of ModSecurity was not compiled with GeoIP or MaxMind support.  in /etc/nginx/nginx.conf:40
nginx: configuration file /etc/nginx/nginx.conf test failed

这是因为ModSecurity中的IP检测的功能还没有添加,暂时可以先不考虑这个,直接删掉或者重命名掉不加载即可:

mv /etc/nginx/owasp/rules/REQUEST-910-IP-REPUTATION.conf /etc/nginx/owasp/rules/REQUEST-910-IP-REPUTATION.conf.bak

测试无误后重启nginx服务,务必使用restart而不是reload等操作。

三、测试

3.1 正常访问

maqian@Inspiron:~$ curl http://localhost -I
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Sat, 01 Jun 2019 13:00:38 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Sat, 01 Jun 2019 12:34:27 GMT
Connection: keep-alive
ETag: "5cf270d3-264"
Accept-Ranges: bytes

返回200,正常访问。

3.2 简单SQL注入

maqian@Inspiron:~$ curl 'http://localhost/?id=1 AND 1=1' -I
HTTP/1.1 403 Forbidden
Server: nginx/1.12.2
Date: Sat, 01 Jun 2019 13:01:30 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

返回403,禁止访问。

3.3 简单xss攻击

maqian@Inspiron:~$ curl 'http://localhost/?search=<scritp>alert('xss');</script>' -I
HTTP/1.1 403 Forbidden
Server: nginx/1.12.2
Date: Sat, 01 Jun 2019 13:01:15 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

返回403,禁止访问。

四、日志

默认情况下,ModSecurity是开启了审计日志的,会把审计日志打到modsec_audit.log,以下就是上面执行xss攻击时候的日志:

---EmbUKKWv---A--
[01/Jun/2019:21:01:15 +0800] 155939407569.849497 127.0.0.1 58856 127.0.0.1 80
---EmbUKKWv---B--
HEAD /?search=<scritp>alert(xss);</script> HTTP/1.1
Host: localhost
User-Agent: curl/7.58.0
Accept: */*

---EmbUKKWv---D--

---EmbUKKWv---F--
HTTP/1.1 403
Server: nginx/1.12.2
Date: Sat, 01 Jun 2019 13:01:15 GMT
Content-Length: 169
Content-Type: text/html
Connection: keep-alive

同时nginx的日志中也有日志:

2019/06/01 21:01:15 [error] 30005#0: *3 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `5' ) [file "/etc/nginx/owasp/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "79"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/"] [unique_id "155939407569.849497"] [ref ""], client: 127.0.0.1, server: localhost, request: "HEAD /?search=<scritp>alert(xss);</script> HTTP/1.1", host: "localhost"
2019/06/01 21:01:30 [error] 30005#0: *4 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Ge' with parameter `5' against variable `TX:ANOMALY_SCORE' (Value: `7' ) [file "/etc/nginx/owasp/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "79"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 7)"] [data ""] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/"] [unique_id "155939409056.181076"] [ref ""], client: 127.0.0.1, server: localhost, request: "HEAD /?id=1 AND 1=1 HTTP/1.1", host: "localhost"

正式环境下,如果访问量很大,建议关闭审计日志,开启审计日志会导致以下问题:

  1. 写入大量的日志数据,会消耗磁盘容量。
  2. 大量的IO操作会影响设备的性能。

关闭审计日志需要在/etc/nginx/modsecurity.conf中添加配置:

SecAuditEngine off

五、优化

5.1 不检查静态内容

location / {
    modsecurity on;
    modsecurity_rules_file /etc/xxxx/main.conf;
    root html;
}
location ~ \.(gif|jpg|png|jpeg|svg)$ {
    root /data/images; 
}

六、参考文档

ModSecurity3_Nginx 指南

nginx下安装配置modsecurity waf防火墙(附完整编译、配置、排错、详细规则)

Nginx1.14.0+ModSecurity实现简单的WAF

一、测试文件状态

shell中的测试,如果成功返回0,否则返回1。

操作符描述
-e FILE测试文件是否存在
-f FILE文件存在且是一个常规的文件为真
-d FILE文件存在且是一个目录为真
-[r,w,x] FILE文件是否可读、可写或者可执行

二、字符串比较

操作符描述
-z STR字符串为空返回真
-n STR字符串不为空返回真
STR1 = STR2字符串相等返回真
STR1 != STR2字符串不相等返回真
STR1 [<,>] STR2字符串STR1小于或者大于STR2返回真,按字典序比较

使用[]判断时,><符号需要加下划线:

> [ "abcde" \> "abcd" ] && echo "yes"
yes

使用[[]]判断时,><符号不需要加下划线:

> [[ "abcde" > "abcd" ]] && echo yes
yes

三、整数比较

操作符描述
INT2 -eq INT2两个整数相等返回真
INT1 -ne INT2两个整数不相等返回真
INT1 -[gt,ge,lt,le] INT2整数大于、大于等于、小于以及小于等于判断

四、注意事项

><符号只能用于字符串比较,不能用于整数比较。对整数使用大于小于符号比较的时候,会被当成字符串:

> [[ 53 -gt 153 ]] && echo yes
> [[ 53 > 153 ]] && echo yes
yes

一、常用端口号

  • 20/21: FTP端口,20端口用来传输数据,21端口用来处理连接
  • 22: ssh端口,安全shell协议
  • 23: telnet端口,telnet服务协议
  • 25/465/587: smtp端口,发邮件协议
  • 53: dns端口,域名解析协议,基于UDP
  • 80: web服务端口,http超文本传输协议
  • 110/995: pop3端口,收邮件协议
  • 115: sftp端口,安全文件传输协议
  • 123: ntp端口,网络时间协议
  • 143/993: IMAP协议端口,收邮件协议
  • 179: bgp端口,边界网络协议
  • 389: ldap端口,轻型目录存储协议
  • 443: https端口,安全超文本传输协议

二、常用网络命令

2.1 ifconfig、ifup和ifdown

启动和关闭网卡:

> ifconfig eth0 up
> ifconfig eth0 down

设置网卡IP地址:

> ifconfig eth0 192.168.10.2
> ifconfig eth0:1 192.168.20.2 # 多IP
> ifconfig eth0 192.168.10.2/24 # ip地址+掩码位数
> ifconfig eth0 192.168.10.2 netmask 255.255.255.0 # ip地址+子网地址
> ifconfig eth0 192.168.10.2/24 broadcast 192.168.10.255 # 指定广播地址

设置IPv6地址:

# 添加IPv6地址
> ifconfig eth0:1 inet6 add abcd::ff10
> ifconfig eth0:2 inet6 add abcd::ff11
# 删除IPv6地址
> ifconfig eth0 inet6 del abcd::ff10/0
> ifconfig eth0 inet6 del abcd::ff11/0

ifupifdown分别用于开启和关闭网卡:

> ifup eth0 # 开启eth0
> ifdown eth0 # 关闭eth0

2.2 route

添加、删除静态路由:

> route [add|del] -net 192.168.1.0 netmask 255.255.255.0 gw 192.168.2.254

设置默认网关:

> route add default gw 192.168.2.254

打印所有的路由:

> route -n # linux
> route print # windows

2.3 dig和nslookup

查询dns命令,dig会打印出解析过程和协议:

> dig www.baidu.com

nslookup打印所有的结果:

> nslookup -t=CNAME www.baidu.com

2.4 traceroute

查询去往一个主机所经过的路由跳数:

> traceroute www.baidu.com

2.5 telnet

一般用于探测服务或端口是否连接正常:

> telnet 129.168.10.1 443

2.6 ethtool

查看和更改网卡的配置

> ethtool -p eth0 # 测试一个网卡,网卡灯会闪烁
> ethtool -s eth0 # 统计网络信息

2.7 netstat

常用参数:

  • -t: 列出TCP套接字
  • -u: 列出UDP套接字
  • -p: 显示PID和进程名字
  • -l: 列出监听状态的socket
  • -n: 使用IP地址而不是解析的域名

查询端口占用情况:

> netstat -apn | grep 443

查询所有处于监听状态的tcp套接字

> netstat -lt

2.8 ping

探测网络是否连通:

> ping www.baidu.com -c 4 # 探测www.baidu.com,只发送四次

windows下默认是发四个包,如果要持续发包使用-t选项:

> ping www.baidu.com -t

2.9 curl和wget

curl命令用来发送get和post请求:

  • -X [POST|DELETE]: http请求类型
  • -H: 添加头部
  • -d: 数据部分
  • -I: 只显示响应头
> curl www.baidu.com
> curl -X POST -H "Content-Type: application/json" \
          -d '{"name": "maqian", "age": 22}' www.baidu.com

wget多用于下载文件:

> wget www.baidu.com/index.html -O baidu.html # -O选项表示写到文件

一、ELF文件

ELF(Executable and Linkable Format)文件是linux下的二进制可执行文件,它同时兼容可执行文件和可链接文件。

一个ELF文件包含两个部分:一个固定长度的文件头和多个可扩展的数据块。其中,文件头是整个可执行文件的总地图,描述了整个文件的组织结构。可扩展数据块分为两类,对应着不同的视图——在链接视图下,数据块的单位是节(Section),用多个节区头索引所有内容;而在执行视图下,数据块的单位是段(Segment),用程序头(Program Header)索引所有的段。如下图所示:

通过readelf -S命令可以打印出文件的节区头部分,这里使用ln命令作为源程序分析:

[ma@localhost:~]$ readelf -S /bin/ls
There are 29 section headers, starting at offset 0x1a358:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400200  00000200
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             000000000040021c  0000021c
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             000000000040023c  0000023c
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400260  00000260
       0000000000000064  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002c8  000002c8
       0000000000000be8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400eb0  00000eb0
       00000000000005c4  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000401474  00001474
       00000000000000fe  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000401578  00001578
       00000000000000a0  0000000000000000   A       6     3     8
  [ 9] .rela.dyn         RELA             0000000000401618  00001618
       00000000000001b0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004017c8  000017c8
       0000000000000990  0000000000000018   A       5    12     8
  [11] .init             PROGBITS         0000000000402158  00002158
       0000000000000018  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000402170  00002170
       0000000000000670  0000000000000010  AX       0     0     4
  [13] .text             PROGBITS         00000000004027e0  000027e0
       000000000000fb68  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000412348  00012348
       000000000000000e  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000412360  00012360
       0000000000003b27  0000000000000000   A       0     0     32
  [16] .eh_frame_hdr     PROGBITS         0000000000415e88  00015e88
       00000000000006b4  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         0000000000416540  00016540
       0000000000001fdc  0000000000000000   A       0     0     8
  [18] .ctors            PROGBITS         0000000000619000  00019000
       0000000000000010  0000000000000000  WA       0     0     8
  [19] .dtors            PROGBITS         0000000000619010  00019010
       0000000000000010  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000619020  00019020
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .data.rel.ro      PROGBITS         0000000000619040  00019040
       0000000000000a48  0000000000000000  WA       0     0     32
  [22] .dynamic          DYNAMIC          0000000000619a88  00019a88
       00000000000001d0  0000000000000010  WA       6     0     8
  [23] .got              PROGBITS         0000000000619c58  00019c58
       0000000000000098  0000000000000008  WA       0     0     8
  [24] .got.plt          PROGBITS         0000000000619cf0  00019cf0
       0000000000000348  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         000000000061a040  0001a040
       0000000000000200  0000000000000000  WA       0     0     32
  [26] .bss              NOBITS           000000000061a240  0001a240
       0000000000000d20  0000000000000000  WA       0     0     32
  [27] .gnu_debuglink    PROGBITS         0000000000000000  0001a240
       0000000000000010  0000000000000000           0     0     4
  [28] .shstrtab         STRTAB           0000000000000000  0001a250
       0000000000000101  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

节区头包含了我们经常见到的字段:.data/.text/.init等,这里只描述一下这几个常见节区的作用。

1.1 .text 节区

该节区存储了源文件编译生成的机器指令。对开发者来说,这可能是最重要的一个节区,所有的程序逻辑都放在这里。但开发者对该节区能做的控制却很少,能影响它的因素只有开发者写的程序逻辑,以及编译时使用的选项。比如,使用 -O1 优化选项编译程序可以生成尽量紧凑的 .text 节区,而用 -O2 优化选项会使编译器倾向于生成执行速度更快的指令组合,但有可能让 .text 节区的体积轻微地增大。

1.2 .rodata 节区

从字面上就能看出,ro表示read only,即不可写,因此该节区存储了程序中的常量数据,例如:

const char *p = "helloworld";

1.3 .data 节区

所有的全局和静态的已初始化变量会存放在这个节区中,这个节区是可读可写的。

1.4 .bss 节区

该节区存储了所有未初始化或初始化为 0 的全局和静态变量,该节区的设计初衷就是为了节省目标文件的存储空间。变量未被初始化,或者虽被初始化了,但值为 0,就没必要浪费空间,再在目标文件中存储大量的 0 值。

1.5 .got 和 .plt 节区

这两个节区存储了动态链接用到的全局入口表和跳转表。当程序中用到动态链接库中的某个函数时,会在该节区内记录相应的数据。

二、进程内存分布

2.1 虚拟地址空间

linux为每个进程都分配了4G(32位平台)的虚拟地址空间,虚拟空间可以认为是操作系统给每个进程准备的沙盒。就像电影《黑客帝国》中 Matrix 给每个人准备的充满营养液的容器一样。实际上,每个进程只存活在自己的虚拟世界里,却感觉自己独占了所有的系统资源(内存)。

当一个进程要使用某块内存时,它会将自己世界里的一个内存地址告诉操作系统,剩下的事情就由操作系统接管了。操作系统中的内存管理策略将决定映射哪块真实的物理内存,供应用使用。操作系统会竭尽全力满足所有进程合法的内存访问请求。一旦发现应用试图访问非法内存,它将会把进程杀死,防止它做“坏事”影响到系统或其他进程。

这样做,一方面为了安全,防止进程操作其他进程或者系统内核的数据;另一方面为了保证系统可同时运行多个进程,且单个进程使用的内存空间可以超过实际的物理内存容量。

该做法的另一结果则是降低了每个进程内存管理的复杂度,进程只需关心如何使用自己线性排列的虚拟地址,而不需关心物理内存的实际容量,以及如何使用真实的物理内存。

2.2 虚拟地址空间分布

虚拟内存的排布规则如下所示:

从下往上依次是0-4G的内存空间,分别分给了不同的区段。可以用一段程序来验证这一个观点:

#include <stdlib.h>

static const char *p = "HelloWorld";
static int s_init_var = 0;
static int s_not_init;

int g_init_var = 0;
int g_not_init;

int main() {
    const char *q = "abc";

    int a, b;
    int *pa = (int *)malloc(sizeof(int));
    int *pb = (int *)malloc(sizeof(int));

    printf("static variable address: %p %p\n",
            &s_init_var, &s_not_init);
    printf("global variable address: %p %p\n",
            &g_init_var, &g_not_init);
    printf("const variable:%p[static] %p\n",
            p, q);
    printf("a and b: %p %p\n", &a, &b);
    printf("pa and pb: %p %p\n", pa, pb);

    return 0;
}

代码分别创建了静态变量、全局变量以及临时变量等多种不同场景下的变量,打印出它们的地址。使用gcc编译运行:

static variable address: 0x600a0c 0x600a10
global variable address: 0x600a08 0x600a14
const variable:0x4006c8 0x4006d3
a and b: 0x7ffda942df14 0x7ffda942df10
pa and pb: 0xa82010 0xa82030

可以看到已经初始化的静态变量s_init_var和已经初始化的全局变量g_init_var两者地址紧紧相邻,间距刚好隔了4个字节,因为他们被初始化了,都放在了.data区域。而未初始化的两个虽然也是相连在一起的,但是和初始化了的变量还是隔了一段距离,但距离不是很大,因为.bss.data地址差别不大。

而被const修饰的两个变量地址也是一样,处在同一个地址空间,和上面的静态变量以及全局变量相比,地址比它们小,这也就说明了.rodata是在.data.bss下面的。

最后的a/bpa/pb则验证了栈区地址和堆区地址的位置,堆是处在靠下的地址,而栈从地址位数上来说就已经很高了。

这里还能看出一点:局部变量b在a后面申请,但是它的地址比a要小;而pb同样也是在pa后面申请,地址却比pa大。
这说明了栈的地址空间是从上往下申请的,而堆则正常由下往上。

三、参考文档

文章部分摘抄于:攻克 Linux 系统编程

原文写得很不错,有兴趣可自行参考。

给nginx配置https时报错:

the "ssl" directive is deprecated, use the "listen ... ssl" directive instead in "nginx.conf:44" 

问题原因

nginx新版本修改了ssl的配置方式,老的ssl on语法已经不推荐使用,建议使用以下形式开启ssl:

listen 443 ssl;

ssl_certificate xx.crt;
ssl_certificate_key xx.key;

低版本nginx开启ssl指令:

ssl on;
ssl_certificate xx.crt;
ssl_certificate_key xx.key;

一、基本用法

Makefile中执行shell命令需要在命令前面加上@,例如打印一个变量的值

KERNEL = $(shell uname -r)
INC = /usr/src/kernels/$(KERNEL)/

print:
    @echo $(KERNEL)
    @echo $(INC)

执行make print会有得到理想的输出:

2.6.32-754.6.3.el6.x86_64
/usr/src/kernels/2.6.32-754.6.3.el6.x86_64/

如果不加@符号最后的结果是:

echo 2.6.32-754.6.3.el6.x86_64
2.6.32-754.6.3.el6.x86_64
echo /usr/src/kernels/2.6.32-754.6.3.el6.x86_64/
/usr/src/kernels/2.6.32-754.6.3.el6.x86_64/

可以看到多了几行,分别把每个echo命令也打印出来了。

二、注意事项

2.1 多行命令

当shell脚本中存在多行命令时,换行时要加上\,例如:

@./configure --prefix=/usr/lib \
    aaa=1 bbb=2

2.2 把shell命令的结果赋值给Makefile变量

把shell命令的值赋值需要加上shell关键字,如:

PWD = @(shell pwd)

一、拓扑图

学习linux的网络框架netfilter,想用centos作为路由器,在下面接PC产生流量测试。

默认情况下linux是没有开启数据包转发功能的,需要手动配置,linux使用centos6.9,网络拓扑图如下:

路由器的eth0口接外网,IP地址192.168.123.102,内网口eth0地址10.0.0.x/24,希望PC通过路由nat上网。

二、转发配置

开启路由转发首先检查好防火墙配置,清空原有的nat规则:

iptable -F

然后添加nat规则,一共有两种方式:

第一种:iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j SNAT --to 192.168.123.102

第二种:iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

其中的部分参数含义为:

  • -t: 类型为nat
  • -A: 添加新规则到规则链的末尾
  • POSTROUTING: 在包就要离开防火墙之前改变其源地址
  • -s: 源地址段,这里设置我的内网地址网段10.0.0.0/24
  • -j SNAT: 满足snat条件的时候跳转
  • --to: 跳转时设置的Ip地址
  • -o: 跳转时的出口设备为eth0

具体可参考防火墙规则,这里设置好后,开启设备的数据包转发:

echo 1 > /proc/sys/net/ipv4/ip_forward

然后快乐的

转发永久生效

上面的echo开启数据包转发只是临时生效,下次重启后就失效了,如果需要永久生效,得修改/etc/sysctl.conf文件,把net.ipv4.ip_forwar设置为1。

> sed -i 's#net.ipv4.ip_forward = 0#net.ipv4.ip_forward = 1#g' /etc/sysctl.conf
> grep net.ipv4.ip_forwar /etc/sysctl.conf # 确认是否修改成功了
net.ipv4.ip_forward = 1 # 已经修改好了
> sysctl -p # 生效配置
net.ipv4.ip_forward = 1 # 这里也可以看到值被修改成了1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296

三、生效性测试

检测是否生效,给PC配置好IP,然后ping百度,同时设备上也开启抓包:

maqian@d2.maqian.co:~$ tcpdump -i eth1 -nnn
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
16:35:42.880920 IP 10.0.0.2.63573 > 223.5.5.5.53: 48582+ A? baidu.com. (27)
16:35:42.924100 IP 223.5.5.5.53 > 10.0.0.2.63573: 48582 2/5/5 A 220.181.57.216, A 123.125.115.110 (229)
16:35:42.938192 IP 10.0.0.2 > 220.181.57.216: ICMP echo request, id 1, seq 11, length 40
16:35:43.021923 IP 220.181.57.216 > 10.0.0.2: ICMP echo reply, id 1, seq 11, length 40
16:35:43.947976 IP 10.0.0.2 > 220.181.57.216: ICMP echo request, id 1, seq 12, length 40
16:35:44.028291 IP 220.181.57.216 > 10.0.0.2: ICMP echo reply, id 1, seq 12, length 40
16:35:44.963608 IP 10.0.0.2 > 220.181.57.216: ICMP echo request, id 1, seq 13, length 40
16:35:45.043968 IP 220.181.57.216 > 10.0.0.2: ICMP echo reply, id 1, seq 13, length 40
16:35:45.979290 IP 10.0.0.2 > 220.181.57.216: ICMP echo request, id 1, seq 14, length 40
16:35:46.058447 IP 220.181.57.216 > 10.0.0.2: ICMP echo reply, id 1, seq 14, length 40
16:35:47.923349 ARP, Request who-has 10.0.0.2 tell 10.0.0.1, length 28
16:35:47.923695 ARP, Reply 10.0.0.2 is-at 00:0c:29:60:22:cc, length 46