简介
记录我在 mac 上用 VisualBox 建立虚拟服务机后通过 nginx 搭建双向验证的网站的过程,包括虚拟机的建立、文件共享设置、虚拟机网络设置、SSH 连接虚拟机、nginx 配置。
Linux Server (ubuntu) 的建立
用 VisualBox 建立一个 Linux Server 虚拟机(ubuntu-16.04.2-server-amd64),建立过程除了存储那边改选了固定大小其它的都是默认的选项。
之后就是安装系统。在虚拟机的设置里选择存储那一项的控制器 IDE 里的光驱选择要装的系统的镜像文件文件,确认后启动虚拟机装好系统。
文件共享
为了实现虚拟机与 mac 文件共享要安装增强功能,主机上根据 ubuntu 的版本下载 VBoxGuestAdditions 镜像文件,然后和装系统时一样地在设置里的存储里增加这个镜像文件,在虚拟机界面的 Devices -> Optical Drives 里确保 VBoxGuestAdditions.iso 是打勾被选中的,然后在虚拟机里运行:
sudo mount /dev/cdrom /media/cdrom
进入 /media/cdrom 里面可以看到一些可执行文件,执行:
sudo ./VBoxGuestAdditions.run
重启虚拟机:
sudo reboot
在虚拟机的设置里创建一个固定分配共享文件夹:
共享文件夹自动挂载在虚拟机的 /media 目录下,名称是 sf_ 加上共享文件夹的名称。这时访问共享文件目录非 root 用户会出现权限问题(一般登录的都是非 root 用户):
运行以下命令:
sudo usermod -aG vboxsf 当前登录的用户名 # 如:sudo usermod -aG vboxsf cxswow
重启虚拟机生效:
sudo reboot
共享文件完成。
网络设置:NAT+Host-Only
虚拟机设置里默认的网络是只有一张使用 NAT 的网卡,这时虚拟机可以上网,但是主机和虚拟机不互通,因此我要加一张网卡来让主机可以访问虚拟机,用的 Host-Only 模式。
在虚拟机关机的情况下打开虚拟机的设置 -> 网络,网卡1默认的不用修改,打开网卡2,启用网卡2的网络连接,连接方式选 Host-Only,
这里要有一个界面名称,如果没有可选的,在 VirtualBox 的设置 -> 网络里的 Host-Only 界面增加一个,用默认的设置就行。
打开虚拟机,编辑网络设置文件:
sudo vim /etc/network/interfaces
我的文件初始内容是这样的:
这里的 auto
后面跟的是网卡名字,之后就是对这个网卡的一些设置。如果不知道对应的网卡的名字,运行以下命令可以查看当前网卡的信息:
ip link
这里可以看到新增加的网卡名字(我的是 enp0s8),所以我的网络配置文件修改成以下这样:
重启虚拟机网络:
sudo /etc/init.d/networking restart
主机通过 ssh 连接虚拟机
通过密码连接
确保虚拟机和客户端都装了 OpenSSL 的相关软件后——如果在主机上用 ssh 连接虚拟机被拒绝了(Connection refused)一般就是虚拟机没有安装 ssh,虚拟机安装 ssh 命令: sudo apt-get install openssh-client openssh-server
——主机直接通过终端的 ssh 命令就能连上服务端:
ssh 虚拟机用户名@虚拟机 IP 地址 # 如:ssh cxswow@192.168.56.101,这个 IP 是 Host-Only 的那个 IP
第一次连接一个虚拟机的时候会警告你要连接的这个机之前未知,让你确认是否继续连接,输入 yes
继续连接。然后会让你输入该机该用户的密码,输入正确后就连接成功,此时就可以像在虚拟机的终端里一样地使用了。
退出连接输入:
exit
使用别名连接
每一次连接都输入用户名和 IP 是一件比较繁琐的事,可以通过配置文件来给固定的用户名和 IP 对起别名,之后通过别名连接会方便点。
做法是在客户端的用户根目录下的 .ssh 文件夹里建立 config 文件,文件内容如下:
Host myUbuntu
User cxswow
HostName 192.168.56.101
之后就可以通过下面的命令来连接了:
ssh myUbuntu
使用证书连接
每次都输入密码也不是一个好的选择,通过密钥认证连接会方便安全点。
在客户端的用户根目录下的 .ssh 目录下运行以下命令生成密钥对:
ssh-keygen -b 1024 -t rsa
这里可选项 -b
后面跟的是密钥的长度 1024 字节,最长 4096 字节,长度影响解密时间,一般 1024 或 2048 就足够了;可选项 -t
后面跟的是加密方式,默认是 rsa,还要一种 dsa。
生成时会提示输入密钥名字,默认名字是 id_rsa ,我给命名成 mymac 了,这里如果使用自己命名的密钥名字,之后将公钥传到虚拟机上的命令就要指定公钥,每次用 ssh 连接的时候也要指定签名的密钥(如:增加 -i mymac
指定)。
还会让你输入私钥短语,有的话安全点。
之后运行下面语句将公钥传到虚拟机上:
ssh-copy-id -i mymac.pub myUbuntu
-i
后面跟的是对应的公钥,如果创建密钥的时候用了非默认名字,这里就要指定。
然后运行:
ssh -i mymac myUbuntu
实现免密登录。但是这里如果创建密钥对的时候输入了密钥的短语,还是要输入短语的,想要每次连接连短语也不输入的话,可以运行:
ssh-add ~/.ssh/mymac
来输入短语后缓存,之后连接就不用再输入额外的信息了。
nginx 配置
安装 nginx:
sudo apt-get install nginx
/etc/nginx/nginx.conf 就是 nginx 的配置文件,不过一般不将我们的网站的配置放在这里,而是在 /ect/nginx/sites-available 文件夹下,该文件夹下面初始有个演示的例子的配置文件 default,内容如下:
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
#
# Generally, you will want to move this file somewhere, and start with a clean
# file but keep this around for reference. Or just disable in sites-enabled.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php7.0-cgi alone:
# fastcgi_pass 127.0.0.1:9000;
# # With php7.0-fpm:
# fastcgi_pass unix:/run/php/php7.0-fpm.sock;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}
#
后面是注释掉的内容,注释掉的内容很多是 nginx 的标准配置方法,去掉注释的内容后实际内容如下:
server {
# 默认监听本机80端口
listen 80 default_server;
listen [::]:80 default_server;
# 指名 html 文件夹所在
root /var/www/html;
# 网站主页
index index.html index.htm index.nginx-debian.html;
# 访问的网址
server_name _;
# 路由详细配置
location / {
try_files $uri $uri/ =404;
}
}
我们可以通过拷贝默认配置文件再在上面修改成我们想要的配置,拷贝命令(假设要配置的网站是 example.com
):
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com
修改配置成:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/example.com/html; # 自建的 html 的文件夹位置
index index.html index.htm; # 该文件夹下要有其中一个文件可以当主页
server_name example.com www.example.com; # 只是好看的网址,之后会说
location / {
try_files $uri $uri/ =404;
}
}
这里要注意的是 server_name
后面跟的网址,如果像上面写的那样要修改 /etc/hosts 文件,该文件是本机访问的网站的名称和 ip 的映射,要用 example.com 这个域名来访问你的网址(本机访问,也就是虚拟机访问,好像这里并没什么用,因为这个虚拟机没有浏览器,好像还是要通过主机的浏览器访问虚拟机的 ip 来访问网站,不太懂)就要在 /etc/hosts 文件里加上一行,大概是这样的:
127.0.0.1 localhost
127.0.0.1 example.com
这样说起来直接写 server_name 127.0.0.1
更方便。
保存该配置文件后启动或重启 nginx 后,主机可以通过访问虚拟机的 ip 来查看网站了,比如之前设置的是 192.168.56.101,那直接访问 192.168.56.101 就可以看到你指定的网站的主页了。
nginx 启动命令: sudo service nginx start
。
nginx 重启命令: sudo service nginx restart
。
查看 nginx 目前情况: ps -ef | grep nginx
。
单向验证的网站
要让这个网站变成 https 的网站要生成自己的 SSL 证书, 创建放证书的文件夹(之后配置文件里要写明证书位置,放在某个地方比较方便管理而已):
sudo mkdir /etc/nginx/ssl
生成证书:
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /etc/nginx/ssl/example.key -out /etc/nginx/ssl/example.crt
生成的时候会问一些问题,按描述填写就行。问题像这样:
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:BJ
Organization Name (eg, company) [Internet Widgits Pty Ltd]:my company
Organizational Unit Name (eg, section) []:tech
Common Name (e.g. server FQDN or YOUR name) []:cxswow
Email Address []:
生成的 example.crt 就是证书文件,example.key 是私钥。
然后把配置文件修改成:
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl; # 新增加,ssl 默认监听的端口就是443,开启监听
root /var/www/example.com/html;
index index.html index.htm;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/example.crt; # 新增,声明证书
ssl_certificate_key /etc/nginx/ssl/example.key; # 新增,声明私钥
location / {
try_files $uri $uri/ =404;
}
}
重启 nginx : sudo service nginx restart
。
然后访问网站,这时候就是走的 https 。
双向验证的网站
双向验证最少要有三方,像是甲方、乙方、见证人这样的。这里一般叫 server、client、CA 。
之前单向验证只生成了 server,而且 server 的证书是自己给自己签名的,这里的 server 证书和 client 证书都要 CA 来签发,之前的证书不能用了。
所以 server 和 client 生成私钥和证书签名请求文件(Certificate Signing Request):
# 生成 server 的私钥和证书签名请求文件,还是放在之前说的 /etc/nginx/ssl 文件夹里面,这里进入该文件夹执行下面的命令
sudo openssl genrsa -des3 -out server.key 1024
sudo openssl req -new -key server.key -out server.csr
# 生成 client 的私钥和证书签名请求文件
sudo openssl genrsa -des3 -out client.key 1024
sudo openssl req -new -key client.key -out client.csr
# 生成 CA 私钥和自签名证书
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout ca.key -out ca.crt
然后生成 ca 认证的 server 证书和 client 证书:
# ca 签发 server 证书
sudo openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
# ca 签发 client 证书
sudo openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
而客户端证书要用在浏览器上,一般要转换成浏览器可以用的格式,这里转成 .p12
格式:
openssl pkcs12 -export -inkey client.key -in client.crt -out client.p12
这里会让你输入密码,是在证书导入浏览器的时候要输入的密码。
p12
格式的文件在主机上知己双击运行安装就行。
修改 nginx 的配置文件,对虚拟机上部署的 server 进行反向代理:
server {
# 开启
ssl_verify_client on;
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl on;
listen 443 ssl default_server;
# Add index.php to the list if you are using PHP
#index index.html index.htm index.nginx-debian.html;
server_name example.com www.example.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 当客户端证书认证通过后,将客户端证书中 Common Name 提取到 X-ClientCert-DN 头里
# 这样上游的 Node.JS Server 就可以从这里知道登录用户的身份了(名字)
proxy_set_header X-ClientCert-DN $ssl_client_s_dn;
}
}
这里 proxy_pass
就是别人访问虚拟机的默认443端口会返回的请求内容,这里要返回本机8080端口的内容,要另外自己开个 server 监听8080端口。
在主机上安装 client.p12
证书(双击文件可以直接安装),再访问 https://192.168.56.101
的时候就可以选择 client.p12
证书来进行验证了。
8080端口的那个 server 也可以根据请求头的 Common Name 来返回不同的内容。
参考:
Guest Additions
File permission issues with shared folders under Virtual Box
VirtualBox 中 ubuntu 的网络设置
SSH原理与运用(一):远程登录
How To Set Up Nginx Server Blocks (Virtual Hosts) on Ubuntu 14.04 LTS
How To Create an SSL Certificate on Nginx for Ubuntu 14.04
Protect your Node.js API with nginx and SSL client certificates
反向代理