提升安全性:Nginx 反向代理 RDP 和 IIS 服务
后续:将提供 webdav 服务的 IIS 替换为 rclone
关联:使用 Zram 作为 Swap,将内存消耗从 750MB 降至 450MB,教程:how-to-enable-the-zram-module-for-faster-swapping-on-linux
Windows 远程桌面服务作为开放给外网的高权限系统组件存在很大的安全隐患,因此如果需要使用 RDP 服务,最好不要直接开放其对应的 3389 端口。通过Remote Desktop Gateway
这个默认存在于rdp客户端的认证通道或者是通过代理来避免端口直接暴露。以上两种方式中前者较麻烦,后者则造成每次连接远程桌面都要开启代理,都不太方便。本文介绍如何用Guacamole
将rdp内容转换为HTTP,再由Nginx
代理给客户端来减少暴露额外的端口带来的安全风险。Nginx
监听 443 端口传入的 HTTPS 协议内容,并按路径转发给 Hyper-v VM Docker 中的Guacamole
和 Windows 上的 IIS 服务端。Guacamole
将宿主机的 RDP 服务转换成 HTML5 内容,不再需要服务端向局域网开放 3389 端口。
本文基于如下环境和程序
Environment | Contains | |
---|---|---|
Windows | IIS Webserver 10.0 | Hyper-v (VM) |
Debian10 VM (Hyper-v) | Nginx 1.14.2 | Docker (Apache Guacamole 1.2.0) |
各程序服务按外部访问流程排序
Program | Usage | Env | Protocol | Address |
---|---|---|---|---|
Nginx | HTTPS reverse proxy | Debian10 VM | HTTPS | 192.168.51.118:443 Lan 192.168.208.146 VM |
IIS Webserver | Other web app (Webdav) | Windows | HTTP | 192.168.208.145:8088/S4f_/ VM |
Guacamole | HTML5 RDP server | Debian10 VM (Docker) | HTTP | 127.0.0.1:8080/rdp/ VM |
RDP | Microsoft remote desktop | Windows | RDP | 192.168.208.145:3389 VM |
覆盖图
graph LR
subgraph "Hyper-V"
subgraph "VM (Debian10)"
c1[Nginx]-->|127.0.0.1
:8080/rdp/|c2[Guacamole
& SSH to self]
end
subgraph "Windows10"
a1["IIS Webserver
(WebDav)"]
a2[RDP]
end
end
b1[用户]-->|HTTPS
192.168.51.118|c1
c1-->|192.168.208.145
:8088/S4f_/|a1
c2-->|192.168.208.145
:3389|a2
No Need To Prepare Config File #
More offcial Docs Here
This Part is for the native build therefore SHOULD BE SKIPPED.1
As we use docker file here, the default user has been set to guacadmin
with password guacadmin
.
Open Firewall for IIS’ Unusual Port #
In Windows
After binding port 8088
to IIS server’s http service, there are addtional steps for unusual port other than 80
and 443
.
Open firewall advance settings, add allow rules for 8080
. In Scope
Remote IP address part, add ip range 192.168.208.0
to 192.168.208.255
. In Advance
Profiles part, check all three checkboxs and confirm.
Install Guacamole by Docker #
In Hyper-v (Debian10)
Docker Site and uploader’s Github
- Add those text to a
Dockerfile
which is used for changing base url.2
|
|
- Run command at the place where you put the
Dockerfile
to bulid a new image.
|
|
- Run the container (replace
/root/guacamole
with your config directory which shouldmkdir
first when new build) from your new-built image.
|
|
Nginx URL Rewrite #
In Hyper-v (Debian10)
My original plan is to use IIS ARR3.0
module to achive the URL rewrite purpose as this IIS webserver also do the job of sharing my files in Lan via Webdav. But after a query online, I find out that the support of Websocket
in IIS which Guacamole
needed to communicate seems lack. Therefore I use Nginx
to do reverse proxy both for Guacamole
in Hyper-V
and Webdav that host on IIS.3
Assign IPs to VM 4 #
To get double IPs in debian VM, first you need to add two network interfaces (here for example eth0
is external for Lan and the eth1
is internal to host) on hyper-v. Then login to the VM and add those lines to /etc/network/interfaces
, eth1
’s static ip is different from the host 192.168.208.145
(like one number after) and mask is same as host’s (you can find those in Windows Cmd ipconfig /all
). This internal ip is related to firewall
settings in Windows so you need to assign one manually to access the host Windows:
|
|
Apply changes by systemctl restart networking
. Type ip a
in command to find your IPs.
In Windows
To add static IP in Windows, open Cmd(Administrator) and input:
|
|
Add this command to Manage
->Scheduled Tasks
->Create a Basic Task
and set it run as administrator
to get static ip rather than random one that Windows assign to on startup. Disable only start task when plug in power
and add a delay for 30s
after startup.
By default, Hyper-v will give VM a random Mac which would make your router assign new ip for your VM’s external network on boot. Open VM’s settings, expand your external Network Adapter
and click on Advanced Features
. In Mac Address
select static
and input a Mac address.
Install Nginx with Webdav module #
In Hyper-v (Debian10)
|
|
Remove Default Page
|
|
Add Proxy Config #
Add those lines to your /etc/nginx/nginx.conf
file inside the server
section.
Replace rdp
with your guacamole
’s url path.
|
|
Replace S4f_
with your IIS Webdav url path, and 192.168.208.145
with your Windows’s internal ip. I also changed the query string from client to IIS to fix problems (it’s an Nginx bug when reverse proxy a webdav request, especially when use MOVE
method, the Destination
header would be set as nginx’s address rather than IIS’ 5) by .if set
add in http
|
|
add in server
|
|
Config HTTPS and Auth #
About how to generate self-signed certificate, you can refer to Here.
Copy your server.crt
and server.key
to nginx/
folder (use scp /path/to/file username@ip.ip.ip.ip:/path/to/destination
or Winscp
).
You can add extra auth for anyone who visit the site by nginx’s basic auth
6.
Replace server
config with following lines.
|
|
And to disable default nginx index page, add in server
:
|
|
As a summary, your nginx.conf
file should look like this:
|
|
When done, restart your Nginx service:
|
|
Manage Nginx and Guacamole to Autostart #
For Nginx
:
|
|
To check port use lsof -i -P
.The Nginx log is in /var/log/nginx/
as access.log
and error.log
for debug.
For Guacamole
, in /etc/systemd/system/docker-win10rdp.service
add:
|
|
Enable the service:
|
|
Edit Guacamole Settings #
In Browser
Add Account #
Access Guacamole
’s web page using your VM’s external ip address https://192.168.51.118/rdp
with guacadmin
for both username and password.
After a successfully login, open settings and click on the Users
. Create your own account with all permissions checked. Sign in your own account and delete the default guacadmin
one.
Add Connections #
Click on the New Connection
inside the Connections
. Fill the Name, Protocol, Hostname (Hyper-v internal ip), Port, Username, Password, Security Mode and check the Ignore server certificate
. Save the changes.
In Hyper-v (Debian10)
If you want to enhance VM server’s security, you can add a ssh
connection from docker to host. Use ip a
to check docker0
host’s ip. Also, change the settings in /etc/ssh/sshd_config
by change port
and listenAddress
. Apply changes sudo systemctl restart sshd
:
|
|
Also add these command after sudo systemctl edit sshd
to allow sshd
start when docker0
’s ready:
|
|
Test on Connection #
In Browser
Back to Home
, click on the connection you just added. Check if everything is fine.
More Security Stuff #
Expose 3389 to Hyper-v only #
In Windows
Open firewall advance settings, add two allow rules for TCP and UDP port3389
. In Scope
Remote IP address part, add ip range 192.168.208.0
to 192.168.208.255
. In Advance
Profiles part, check all three checkboxs and confirm. Disable old TCP and UDP port 3389
allow rules.
Fail x00/x03 to Ban #
In Hyper-v (Debian10)
Install fail2ban
|
|
Edit your config file from template
|
|
Add your client ip to ignoreip
|
|
Modify default bantime
, findtime
and maxretry
|
|
Create your special jail in jail.local
|
|
Create filter for Nginx x0x (like 403) error
|
|
Add
|
|
You can test your regex here
|
|
Restart fail2ban
|
|
To see status
|
|
To rescue from jail
|
|
APPENDIX #
Deprecated
Changing the config of Guacamole is now in its GUI control panel instead of in its config file.First, prepare the config file that will be used by
guacamole
.
Replace192.168.208.145
with yourWindows
IP address orHostname
(not recommend) connected to theHyper-v
(as192.168.208.146
is the VM’s IP thatHyper-v
assigned to). Also don’t forget to change theusername
andpassword (md5)
.
InDebian
, you can usemd5sum <File contains PASSWORD>
to hash your plain password.
/root/guacamole/user-mapping.xml ```xml
↩︎<!-- Another user, but using md5 to hash the password (example below uses the md5 hash of "PASSWORD") --> <authorize username="admin" password="**PASSWORD CONVERT TO MD5**" encoding="md5"> <connection name="Unique Name"> <protocol>rdp</protocol> <param name="hostname">192.168.208.145</param> <param name="port">3389</param> </connection> </authorize> </user-mapping> ```
Failed
Failed onCOPY root /
.(This file is originated from
oznu/guacamole
, however the path incurl -SLo ${CATALINA_HOME}/webapps/ROOT.war
has been changed to${CATALINA_HOME}/webapps/rdp.war
which is same tohttps://.../rdp
).
↩︎```dockerfile FROM library/tomcat:9-jre11 ENV ARCH=amd64 \ GUAC_VER=1.2.0 \ GUACAMOLE_HOME=/app/guacamole \ PG_MAJOR=9.6 \ PGDATA=/config/postgres \ POSTGRES_USER=guacamole \ POSTGRES_DB=guacamole_db # Apply the s6-overlay RUN curl -SLO "https://github.com/just-containers/s6-overlay/releases/download/v1.20.0.0/s6-overlay-${ARCH}.tar.gz" \ && tar -xzf s6-overlay-${ARCH}.tar.gz -C / \ && tar -xzf s6-overlay-${ARCH}.tar.gz -C /usr ./bin \ && rm -rf s6-overlay-${ARCH}.tar.gz \ && mkdir -p ${GUACAMOLE_HOME} \ ${GUACAMOLE_HOME}/lib \ ${GUACAMOLE_HOME}/extensions WORKDIR ${GUACAMOLE_HOME} # Install dependencies RUN apt-get update && apt-get install -y \ libcairo2-dev libjpeg62-turbo-dev libpng-dev \ libossp-uuid-dev libavcodec-dev libavutil-dev \ libswscale-dev freerdp2-dev libfreerdp-client2-2 libpango1.0-dev \ libssh2-1-dev libtelnet-dev libvncserver-dev \ libpulse-dev libssl-dev libvorbis-dev libwebp-dev libwebsockets-dev \ ghostscript postgresql-${PG_MAJOR} \ && rm -rf /var/lib/apt/lists/* # Link FreeRDP to where guac expects it to be RUN [ "$ARCH" = "armhf" ] && ln -s /usr/local/lib/freerdp /usr/lib/arm-linux-gnueabihf/freerdp || exit 0 RUN [ "$ARCH" = "amd64" ] && ln -s /usr/local/lib/freerdp /usr/lib/x86_64-linux-gnu/freerdp || exit 0 # Install guacamole-server RUN curl -SLO "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VER}/source/guacamole-server-${GUAC_VER}.tar.gz" \ && tar -xzf guacamole-server-${GUAC_VER}.tar.gz \ && cd guacamole-server-${GUAC_VER} \ && ./configure \ && make -j$(getconf _NPROCESSORS_ONLN) \ && make install \ && cd .. \ && rm -rf guacamole-server-${GUAC_VER}.tar.gz guacamole-server-${GUAC_VER} \ && ldconfig # Install guacamole-client and postgres auth adapter RUN set -x \ && rm -rf ${CATALINA_HOME}/webapps/ROOT \ && curl -SLo ${CATALINA_HOME}/webapps/rdp.war "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VER}/binary/guacamole-${GUAC_VER}.war" \ && curl -SLo ${GUACAMOLE_HOME}/lib/postgresql-42.1.4.jar "https://jdbc.postgresql.org/download/postgresql-42.1.4.jar" \ && curl -SLO "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VER}/binary/guacamole-auth-jdbc-${GUAC_VER}.tar.gz" \ && tar -xzf guacamole-auth-jdbc-${GUAC_VER}.tar.gz \ && cp -R guacamole-auth-jdbc-${GUAC_VER}/postgresql/guacamole-auth-jdbc-postgresql-${GUAC_VER}.jar ${GUACAMOLE_HOME}/extensions/ \ && cp -R guacamole-auth-jdbc-${GUAC_VER}/postgresql/schema ${GUACAMOLE_HOME}/ \ && rm -rf guacamole-auth-jdbc-${GUAC_VER} guacamole-auth-jdbc-${GUAC_VER}.tar.gz # Add optional extensions RUN set -xe \ && mkdir ${GUACAMOLE_HOME}/extensions-available \ && for i in auth-ldap auth-duo auth-header auth-cas auth-openid auth-quickconnect auth-totp; do \ echo "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VER}/binary/guacamole-${i}-${GUAC_VER}.tar.gz" \ && curl -SLO "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/${GUAC_VER}/binary/guacamole-${i}-${GUAC_VER}.tar.gz" \ && tar -xzf guacamole-${i}-${GUAC_VER}.tar.gz \ && cp guacamole-${i}-${GUAC_VER}/guacamole-${i}-${GUAC_VER}.jar ${GUACAMOLE_HOME}/extensions-available/ \ && rm -rf guacamole-${i}-${GUAC_VER} guacamole-${i}-${GUAC_VER}.tar.gz \ ;done ENV PATH=/usr/lib/postgresql/${PG_MAJOR}/bin:$PATH ENV GUACAMOLE_HOME=/config/guacamole WORKDIR /config COPY root / EXPOSE 8080 ENTRYPOINT [ "/init" ] ```
Deprecated
Use nginx reverse proxy instead because ARR’s lack of Websocket support.Install Application Request Routing (ARR) Module
InstallARR 3.0
forReverse Proxy
support. You can install the ARR module directly from the location: http://www.microsoft.com/web/gallery/install.aspx?appid=ARRv3_0.Change URL Rewrite Settings
Open theIIS Manager
and open theDefault Web Site
config pannel. Then double click on theURL Rewrite
option.
Add 2 rules forInbound rules
and another one forOutbound Rules
by clicking on theAdd Rule(s)...
action and Choose theBlank Rule
template. Contents that needed to be changed are in the following tables:Inbound Rules
The first line of the table prevent address/S4f_/
(another web app that hosted on the IIS server) to be rewritten to therdp
sevice. Also need to check the Append query string checkbox.
↩︎|Name|Input|Match|Pattern|Action Type|Action URL|Stop Processing| |-|-|-|-|-|-|-| |S4f| - |match|S4f_(/?)(.*)|Rewrite|{R:0}|True| |rdp| - |match|rdp(/?)(.*)|Rewrite|http://172.17.111.38:8080/{R:0}|True|
Deprecated
The hostname that Hyper-v provided is not stable. It’s better set static ip both manually.The old host-accessed-by-hostname (
HOSTNAME.mshome.net
) plan isn’t unstable enough than assigning static ip to both host192.168.208.145
and VM192.168.208.146
. ↩︎Important
There is a bug when Nginx handle reverse proxy to a webdav server. Related discussion and webdav path requirement.In Nginx’s reverse proxy part
S4f_
, I add extra filter to change query uri for webdav service. I’m usingrclone
as my Webdav client,however, it contains bugs while sending requests to a proxyed server. For example,rclone
sends webdav methodMOVE
and query urihttps://<nginx>/S4f_/path/to/file
to the server to rename a file while the correct query uri without proxy ishttp://<IIS>/S4f_/path/to/file
.Therefore, to fix the problem, I add extra if-statement and regex.set
to convert path that contains scheme and server name to relative path
Edit1: It seems that it’s a common problem when use Nginx as reverse proxy and not just the bug of rclone. More information about this is in description above. The Webdav server will check theDestination
header in query uri if it match the internal server.
Edit2: Because of RFC2518,Destination
header should be absolute URI. Convert it to relative is not the solution. The better way is to rewrite the scheme and path to match the internal communication when proxy to IIS backend.
Edit3:if-state
in Nginx will double escape() the query string (like convert already converted string%6E
to%256E
). So I usemap
(should be placed inhttp
section) instead. For some reason,IIS Webdav
server behind proxy will check ifMOVE
method’s query stringDestination
is match the proxyedHost
. To avoid error, delete or comment theproxy_set_header host $http_host
inlocation /S4f_/ {..}
. ↩︎Optional
Set basic auth for Nginx.Generate your
conf/userpass.txt
file by commandhtpasswd
or onlineMD5 htpasswd
generator. Text inside are likeshare:$apr1$AOW5qtOx$7D3rsVK/jIQuQYqd11/Xg0
. Add those lines in yournginx.conf
server
section.nginx auth_basic "Auth"; auth_basic_user_file userpass.txt;
↩︎
时空乱流记录