We’re going to be setting up NGINX to use LET’S ENCRYPT and for this example we’re going to be pointing to a Guacamole server, which requires zero buffering of packets as it is using real time RDP data. So the config will take that into account. Additionally, we’re shooting for the highest rating possible on SSL Labs and only supporting the latest encryption standards!
UPDATE: I’ve gotten two reports that the cron job is failing so I changed it to what is recommended by Juan in the comments below
We’re going to start with 2 assumptions:
#1. You have configured your external DNS to point to this server…
#2. You’re port forwarding ports 80 and 443 to this server…
Let’s set a few variables for our install
EXTERNALFQDN=guac.domain.com INTERNALFQDN=guac01.domain.local
EXTERNALFQDN is the external fully qualified domains name we’re passing traffic for
INTERNALFQDN is the internal host name of your Guacamole server
Install NGINX and Let’s Encrypt:
apt-get -y install nginx letsencrypt openssl
Make a directory to store your certs
mkdir -p /etc/nginx/ssl/$EXTERNALFQDN
Create a 4096 bit Diffie-Hellman Key (Go big or go home!)
openssl dhparam -out /etc/nginx/ssl/$EXTERNALFQDN/dhparam.pem 4096
Now let’s configure NGINX. Careful of the formatting here. ssl_ciphers should be one line if you copy and paste
cat > /etc/nginx/nginx.conf <<- EOM user www-data; worker_processes 4; pid /run/nginx.pid; events { worker_connections 768; } http { # My Certificates #ssl_certificate /etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem; #ssl_certificate_key /etc/nginx/ssl/$EXTERNALFQDN/privkey.pem; # SSL Performance Related ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # SSL Protocols and Ciphers ssl_prefer_server_ciphers on; ssl_protocols TLSv1.2; ssl_ciphers "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:!AES128:!aNULL:!MD5:!eNULL:!EXPORT:!DES:!PSK:!RC4"; # DHE Key-Exchange ssl_dhparam /etc/nginx/ssl/$EXTERNALFQDN/dhparam.pem; # Random Security Stuff server_tokens off; add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security max-age=63072000; # Common Proxy Settings proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; ######################## # Default Config Stuff # ######################## access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; #Default:2048 include /etc/nginx/mime.types; default_type application/octet-stream; gzip on; gzip_disable "msie6"; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; # REDIRECTS ALL PORT 80/HTTP to 443/HTTPS server { listen 80; listen [::]:80; server_name $EXTERNALFQDN; location ~ /.well-known/acme-challenge { root /var/www/html/; } #return 301 https://$host$request_uri; } # GUACAMOLE SERVER SETTINGS server { listen 443 ssl; listen [::]:443 ssl; server_name $EXTERNALFQDN; proxy_buffering off; proxy_redirect off; proxy_cookie_path /guacamole/ /; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; location ~ /.well-known/acme-challenge { root /var/www/html/; } location / { proxy_pass http://$INTERNALFQDN:8080/guacamole/; } } } EOM
Restart NGINX
service nginx restart
Request a cert from Let’s Encrypt
letsencrypt certonly -a webroot --webroot-path=/var/www/html -d $EXTERNALFQDN --rsa-key-size 4096
Link the Let’s Encrypt public and private keys to the NGINX Config
ln -s /etc/letsencrypt/live/$EXTERNALFQDN/fullchain.pem /etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem ln -s /etc/letsencrypt/live/$EXTERNALFQDN/privkey.pem /etc/nginx/ssl/$EXTERNALFQDN/privkey.pem
Update the NGINX config to use those certs and enforce 443 with redirection
sed -i 's/#ssl_certificate/ssl_certificate/g' /etc/nginx/nginx.conf sed -i 's/#return 301/return 301/' /etc/nginx/nginx.conf
Restart NGINX
service nginx restart
Now we need to setup a cron job that runs every Sunday to check for certs that are near expiration and renew them automatically!
(crontab -l 2>/dev/null; echo '@weekly (certbot renew && service nginx restart) 2>&1 >/dev/null') | crontab -
Hi Chase-
First of all Thanks for the awesome Guacamole install script. That worked wonders and I had a Guac server setup in no time.
Now, I am following this document and I am running into a few issues.
1. I couldn’t run the CAT command and I had to manually edit the nginx.conf file and add the data to it. No big deal it works.
2. Environmental variables. I set them in the /etc/environment file and rebooted. Tested that they are there and I can ping from the box to the variables I set.
When I go to restart NGINX after editing the file I get an error.
Dec 06 09:37:02 guac nginx[5871]: nginx: [emerg] unknown “internatlfqdn” variable
Dec 06 09:37:02 guac nginx[5871]: nginx: configuration file /etc/nginx/nginx.conf test failed
I edited the file and I changed the ‘internalfqdn’ to the internal IP address of the machine and I was then able to restart NGINX.
3. Now; here’s where I am stuck. I try running the next command and it fails…..
Failed authorization procedure. guac.external.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://guac.external.com/.well-known/acme-challenge/5iJrJy01gsX-cP_HTOJ74u9O0czJ22sTQlkhm9vY1hU: “Apache Tomcat/8.0.32 (Ubuntu) – Error reportH1 {font-family:Tah”
IMPORTANT NOTES:
– The following errors were reported by the server:
Domain: guac.external.com
Type: unauthorized
Detail: Invalid response from http://guac.external.com/.well-known
/acme-challenge/5iJrJy01gsX-cP_HTOJ74u9O0czJ22sTQlkhm9vY1hU:
“Apache Tomcat/8.0.32 (Ubuntu) –
Error reportH1 {font-family:Tah”
To fix these errors, please make sure that your domain name was
entered correctly and the DNS A record(s) for that domain
contain(s) the right IP address.
I’ve tried so many things that I’ll not list them to see if you perhaps have a quick answer to my issue.
Thanks!
Noel
Hey Noel, I am having the same issue. Did you happen to find a solution?
Just in case anyone has this same issue, my problem was that my ISP was blocking ports.
Hey There-
Just wanted to come back and tell you that I figured the issue out. ๐
I had redirected port 80 to port 8080 on my FW to test Guacamole.
I went and change the redirection to reflect port 80 going to port 80 and Viola!
Thanks again for some awesome documentation!
Cheers!
Noel
Chase,
For whatever reason my version of the letsencrypt client does not have the –pre and –post-hook commands and for that matter the –quiet. I used this instead for my crontab entry:
(service nginx stop && letsencrypt renew && service nginx start) 2>&1 >/dev/null
Otherwise great post. Thanks.
There’s typo preventing from proper working: internaTlfqdn instead of internalfqdn.
After correction works like a charm.
Thanks.
Thanks! Fixed
So this all works well except now at renewal time, it fails to renew. Guac still works because the cert has not expired. It’s not the script because the same thing happens if I manually stop nginx and issue the renew command. How did it issue the cert in the first place? Any insight would be appreciated. Thanks.
All renewal attempts failed. The following certs could not be renewed:
/etc/letsencrypt/live/mydomain.org/fullchain.pem (failure)
1 renew failure(s), 0 parse failure(s)
IMPORTANT NOTES:
– The following errors were reported by the server:
Domain: mydomain.org
Type: connection
Detail: Could not connect to mydomain.org
I think what you need to do is stop NGINX, comment out the line
return 301 https://$host$request_uri;
, start NGINX, run the renewal. Then put it back…try it, let me know if that works for you.Thanks!
That did the trick. Kinda defeats automatic renewal though.
I agree, I need to create a better automated process ๐ I don’t use this personally so I’ve never had motivation to fix it.
I was able to automate it without messing with the nginx config file. Just use something like this in your crontab entry:
37 */12 * * * (service nginx stop && letsencrypt –standalone renew && service nginx start) 2>&1 >/dev/null
Thanks Juan.
I wanted to let you know that I have been working with users on the Guac forums and we isolated a problem that was a result of the Nginx config you have displayed here.
To summarize…
All of our connections were posting errors in the logs….
11:10:59.020 [http-nio-8080-exec-57] INFO o.a.g.t.h.RestrictedGuacamoleHTTPTunnelServlet – Using HTTP tunnel (not WebSocket). Performance may be sub-optimal.
This was causing lag when a user would open more than 3 connections in multiple tabs in the web browser.
One of the senior guys noticed that the Nginx config had this…
proxy_set_header Upgrade $http_upgrade;
He asked me why the “” before the variable. So I removed it and all logs about reduced performance was gone, and all lag in the browsers was gone.
I just wanted to give you a heads up. Thanks again for your Guac info you post.
Hey that’s good to know! Thanks for pointing that out. My real config on my own device doesn’t have the backslash. I’m not sure how it got in there…
EDIT: Actually it looks like you copied and pasted instead of running the script? In my script the backslash is before the $ because it’s being used in the bash shell as a literal string through “cat” to write to the file. So if you use the script it will print only $ but if you copy and paste you end up with the $
We absolutely copy and pasted the config. Thanks for the info.
No problem. Glad I could help someone ๐
thank you so much for a great resource.
when I use Nginx with my Guacamole Server. I can no longer print to the Guacamole Printer. I get “Fail – Network Error” in chrome or any other browser.
it opens the save to location, lets me enter a name, but then gets the failed – network error.
any suggestions?
I had problems with printing to pdf and downloading files on 0.9.13 and tomcat8. After downgrading tomcat to version 7 it worked for me.
Hi
I have been trying to renew my LE cert for Guac and have commented out the line:
# return 301 https://$host$request_uri; in nginix.conf file
and it still seems to do a redirect and the cert renewal is failing. Am I missing something?
Jeff
Did you restart NGINX after commenting out?
I did restart yes. It turned out to be a weird browser cache issue that was making it appear that redirect was still active. I was able to get the cert renewed successfully.
Hey Chase, could you please help me to extend the configuration to connect to a guacamole Server and an Exchange OWA Server with letsEncrypt and Nginx? Thanks
I’m getting this message when I try to restart NGINX:
nginx.service – A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: failed (Result: exit-code) since Fri 2018-02-02 04:28:03 PST; 23s ago
Process: 3816 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=1/FAILURE)
Feb 02 04:28:03 guac01 systemd[1]: Starting A high performance web server and a reverse proxy server…
Feb 02 04:28:03 guac01 nginx[3816]: nginx: [emerg] invalid number of arguments in “server_name” directive in /etc/nginx/ngi
Feb 02 04:28:03 guac01 nginx[3816]: nginx: configuration file /etc/nginx/nginx.conf test failed
Feb 02 04:28:03 guac01 systemd[1]: nginx.service: Control process exited, code=exited status=1
Feb 02 04:28:03 guac01 systemd[1]: Failed to start A high performance web server and a reverse proxy server.
Feb 02 04:28:03 guac01 systemd[1]: nginx.service: Unit entered failed state.
Feb 02 04:28:03 guac01 systemd[1]: nginx.service: Failed with result ‘exit-code’.
Any help?
Feb 02 04:28:03 guac01 nginx[3816]: nginx: [emerg] invalid number of arguments in โserver_nameโ directive in /etc/nginx/ngi
Feb 02 04:28:03 guac01 nginx[3816]: nginx: configuration file /etc/nginx/nginx.conf test failed
I’d start there
Having issues and dont know where to start. I copy/pasted but was careful on the formatting. Listed below is my nginx conf file.
#1. You copied and pasted the config instead of the entire command as written.
#2. Run “nginx -t” and it will test the config file and tell you what lines are broken.
Your primary issue is #1. Where you have escaped some of the variables (indicated by the use of the dollar sign, $)
~$ sudo nginx -t
nginx: [emerg] BIO_new_file(“/etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem”) failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen(‘/etc/nginx/ssl/$EXTERNALFQDN/fullchain.pem’,’r’) error:2006D080:BIO routines:BIO_new_file:no such file)
I set the variables for EXTERNALFQDN beforehand so should I just write it out explicity instead of relying on variables
You set them but when you copied and pasted you didn’t allow the terminal to expand them into whatever you set them to. Instead you’ve got a literal $VARIABLE. So yes, go edit your configuration file and point to the correct paths…
Lol. I see what you mean now by 1. Looks like I have some work to do. You can delete the prior reply. Thanks again
Thanks a million. I was able to get it sorted out. Last question where is the usermapping file that guacamole uses to store mysql database settings. I need to delete a user mapping ??
Instructions worked like a charm, thank you for writing that out for us!
Best regards,
Frank
The script for nginx has 2 variables that are undefined before they are used.
In the section – Common Proxy Settings
$remote_addr;
and
$proxy_add_x_forwarded_for
are not set before they are used so in the resultant /etc/nginx/nginx.conf both
fail on proxy_set_header because they lack the 2nd argument
I didn’t see anywhere in the post where it mentioned these 2 and I am wondering how
people could have gotten this to work ?
see…
# Common Proxy Settings
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
They’re embedded variables. See (https://nginx.org/en/docs/http/ngx_http_proxy_module.html) and (https://nginx.org/en/docs/http/ngx_http_core_module.html). You don’t need to define them…
Thanks I was wondering how that could’ve worked if they were undefined.