Wednesday, March 25, 2015

IPsec 和 NAT 的冲突问题详解

背景

IPsec 协议可以用来在 IP 层提供校验和加密等安全特性。基于 IPsec 的 VPN 已经成为 site-site 模式下高可靠连通方案的首选。
NAT 最初是为了解决地址不足的问题,它将 NAT 网关后的实际地址隐藏起来,对外呈现一个外部地址,通常包括基于地址的映射(设备负责将内外地址进行对应和替换)和基于端口(通常外部为同一地址,根据到达端口的不同替换为内部不同的地址和端口)的映射。
两者都是常见的网络技术,然而当 IPsec 处理位于 NAT 处理后工作的时候往往会产生冲突。

IPsec

首选,IPsec 支持两种模式:传输和隧道。传输模式只对 IP 包负载应用IPsec协议,对 IP 包头不进行任何修改,因此只能应用于主机对主机的 IPsec VPN 中。隧道模式中 IPsec 将原有的整个 IP 网包封装成带有新的 IP 头部的网包,这样就隐藏了真实的网包信息。隧道主要应用于主机到网关的远程接入的情况。
IPsec 协议支持两种头部:Authentication Header(AH) 和 Encapsulation Security Payload。前者只可以进行认证,后者则即可以加密也可以认证。

传输模式

下图展示了传输模式下原始网包和封装后网包的格式。
transfer
隧道模式 下图展示了隧道模式下原始网包和封装后网包的格式。
tunnel

冲突总结

  • 首先,由于 NAT 往往需要修改包头中的地址(出去的时候修改源地址、进来的时候修改目的地址),这就让 AH 的认证失败了。因为 AH 头验证的是头部+负载的整个网包。但是 ESP 头则不受影响,因为它不检查 IP 包头信息。
  • 校验和。IP 校验和只校验 20 字节的 IP 头;而 ICMP 校验和覆盖整个报文(ICMP 报头 + ICMP 数据);UDP、TCP校验包括整个报文,而且还有 12 字节的 IP 伪首部,包括源 IP 地址(4字节)、目的 IP 地址(4字节)、协议(2字节,第一字节补0)和 TCP/UDP 包长(2字节)。那么,在传输模式下,ESP 加密和验证的 TCP/UDP 头信息,跟外面的 IP 包头信息就对不上了。原始网包中目标地址是对方的外部地址,而收到的网包目标地址则是内部地址了。
  • IKE 架构中的地址往往不允许修改,端口也是固定的,这跟 NAT 冲突。
  • 同一个 NAT 后的设备在外面看起来往往是来自同一个外部地址,而后面可能有多个 VPN 用户,这将导致协商阶段的冲突。
  • 应用层网关,因为负载被加密了,自然无法解析和修改,导致失败。
  • NAT 超时后,导致 VPN 连接失败。
  • 其它,包括对分片包的处理等。

解决思路

首先 AH 头因为是对整个网包的认证,所以是无法跟 NAT 共存的。而 ESP 头只保护 IP 包头以外的内容,因此是可能跟 NAT 设备共存的。
目前,为了解决这一问题,提出了 NAT-T 方案。在 ESP 外面除了新的 IP 包头,再添加一个 UDP 头,这样,外面的 NAT 设备操作的是新的 IP 头和 UDP 头,而跟内部信息无关。
NAT-T 在实际操作中,需要中间 NAT 设备的探测。

参考

Tuesday, March 10, 2015

OpenVPN 配置

服务器端

安装软件包

sudo aptitude install openvpn openssl openssl-devel pam pam-devel -y

用 easyrsa 来生成配置的证书和密钥等文件

获取 easyrsa

git clone https://github.com/OpenVPN/easy-rsa
参考 easyrsa 的说明文件 来生成如下各个文件 (最新版本命令略有不同,但过程是一致的)。
文件名谁需要作用是否需保密
ca.crt服务端 + 所有客户端根 CA 证书
ca.key密钥签名服务器根 CA 密钥
server.crt服务端服务端证书
server.key服务端服务端密钥
dh{n}.pem服务端迪菲·赫尔曼参数
client1.crt客户端1客户端1的证书
client1.key客户端1客户端1的密钥

修改 vars 文件来提前配置一会生成过程中的默认变量

export KEY_COUNTRY="CN"
export KEY_PROVINCE="BJ"
export KEY_CITY="Beijing"
export KEY_ORG="Orgnization"
export KEY_EMAIL="user@some.com"
export KEY_OU="SE"
export KEY_NAME="EasyRSA"
完成后更新到环境变量中
source vars

清理环境

./clean-all

生成 CA 的证书和密钥

./build-ca
基本上一路回车选择默认值即可。

生成服务端证书和密钥

./build-key-server server1

对每个客户端单独生成证书和密钥

./build-key client1

修改服务端的配置文件

一般位于 /etc/openvpn/server.conf,如果不存在可以从 cp /usr/share/openvpn/examples/server.conf 复制一份。
修改其中的各个证书、密钥文件的位置,重启服务。
port 1194
proto udp
dev tun
ca /etc/openvpn/keys/ca.crt
cert /etc/openvpn/keys/kumu_server.crt
key /etc/openvpn/keys/kumu_server.key  # This file should be kept secret
dh /etc/openvpn/keys/dh1024.pem
server 10.8.0.0 255.255.255.0
push "route 192.168.10.0 255.255.255.0" # 推送路由
client-to-client
keepalive 10 120
tls-auth /etc/openvpn/keys/ta.key 0 # This file is secret
comp-lzo
persist-key
persist-tun
status /etc/openvpn/keys/openvpn-status.log
verb 3

配置路由并重启服务

echo 1 > /proc/sys/net/ipv4/ip_forward
openvpn --config /etc/openvpn/server.conf --daemon

客户端

sudo apt-get install -y openvpn
客户端需要如下几个文件:
ca.crt  client1.crt  client1.key  config.ovpn
其中 config.ovpn 需要手动编写,内容为
#config.ovpn
dev tun
client
remote 9.186.100.149
ca ca.crt
cert client1.crt
key client1.key
启动客户端
sudo openvpn --config /path/to/config.ovpn

Friday, March 06, 2015

DevStack 安装 OpenStack 多节点

目前安装 OpenStack 常见的方案有 Redhat 的 RDO 和社区的 DevStack
其中,RDO 功能比较强大,运行也稳定,可以在一个节点上通过一个 answer 文件直接部署多个节点,搭建一套 OpenStack 环境。但是可惜,在 Ubuntu 上还不支持。
DevStack 支持 Ubuntu、Fedora 等环境,需要在每个节点上单独执行,适合进行实验。目前常见的教程一般都是讲解 DevStack 单节点安装。本文讲解最新的 Juno 版本在多节点上的安装过程。

网络环境

两台机器,分为控制节点(同时也作为网络节点)和计算节点。

控制节点

eth0: 9.186.100.77/24 作为管理网络(同时也是公共网络)。 eth1: 10.0.100.77/24 作为内部网络接口。

计算节点

eth0: 9.186.100.88/24 作为管理网络(同时也是公共网络)。 eth1: 10.0.100.88/24 作为内部网络接口。

配置 stack 用户

创建 stack 用户
sudo groupadd stack
sudo useradd -g stack -s /bin/bash -d /opt/stack -m stack
添加 stack 用户权限。
sudo echo "stack ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
切换到 stack 用户
sudo su - stack

下载代码

下载 devstack 代码,并切换到 stable/juno 分支。
sudo apt-get install git -y 
git clone https://git.openstack.org/openstack-dev/devstack -b stable/juno

编写运行配置文件

在 devstack 根目录下,编写 local.conf。
控制节点的 local.conf
[[local|localrc]]

HOST_IP=9.186.100.77 # management network
PUBLIC_INTERFACE=eth0  #public network

FIXED_RANGE=10.0.100.0/24
#FIXED_NETWORK_SIZE=4096
FLOATING_RANGE=9.186.100.0/24
PUBLIC_NETWORK_GATEWAY=9.186.100.1

MULTI_HOST=1
LOGFILE=/opt/stack/logs/stack.sh.log

# Credentials
ADMIN_PASSWORD=admin
MYSQL_PASSWORD=secret
RABBIT_PASSWORD=secret
SERVICE_PASSWORD=secret
SERVICE_TOKEN=abcdefghijklmnopqrstuvwxyz

# enable neutron-ml2-vxlan
disable_service n-net
enable_service q-svc,q-agt,q-dhcp,q-l3,q-meta,q-metering,q-lbaas,neutron,tempest,heat

# OFFLINE=True
计算节点的 local.conf
[[local|localrc]]
HOST_IP=9.186.100.88 # management IP
FIXED_RANGE=10.0.100.0/24
#FIXED_NETWORK_SIZE=4096
FLOATING_RANGE=9.186.100.0/24

MULTI_HOST=1
LOGFILE=/opt/stack/logs/stack.sh.log

# Credentials
ADMIN_PASSWORD=admin
MYSQL_PASSWORD=secret
RABBIT_PASSWORD=secret
SERVICE_PASSWORD=secret
SERVICE_TOKEN=abcdefghijklmnopqrstuvwxyz

# Service information
SERVICE_HOST=9.186.100.77
MYSQL_HOST=9.186.100.77
RABBIT_HOST=9.186.100.77
GLANCE_HOSTPORT=9.186.100.77:9292
Q_HOST=9.186.100.77
KEYSTONE_AUTH_HOST=9.186.100.77
KEYSTONE_SERVICE_HOST=9.186.100.77

CEILOMETER_BACKEND=mongodb

DATABASE_TYPE=mysql
ENABLED_SERVICES=n-cpu,n-net,n-api,c-sch,c-api,c-vol

# vnc config
NOVA_VNC_ENABLED=True
NOVNCPROXY_URL="http://9.186.100.77:6080/vnc_auto.html"
VNCSERVER_LISTEN=$HOST_IP
VNCSERVER_PROXYCLIENT_ADDRESS=$VNCSERVER_LISTEN

# OFFLINE=True

执行配置

执行命令。
./stack.sh
会输出各项操作的结果。日志会写到 stack.sh.log 文件。

其它事项

卸载 openstack
./unstack.sh
清除安装。
./clean.sh
有时候有些文件可能清除不干净,手动执行
sudo rm -rf /etc/libvirt/qemu/inst*
sudo virsh list | grep inst | awk '{print $1}' | xargs -n1 virsh destroy