# how to use acme-client on FreeBSD/nginx



## fred974 (Apr 25, 2017)

Could you please tell me how do you implement letsnencrypt with nginx reverse proxy?
I have installed /security/acme-client and I now need to create an entry simillar to the following but so far no matter where I add this code, nginx do not restart

```
# Letsencrypt needs http for acme challenges
 location ^~ /.well-known/acme-challenge/ {
     proxy_redirect off;
     default_type "text/plain";
     root usr/local/www/acme;
     allow all;
 }
```
Here is my /etc/nginx.conf file

```
load_module /usr/local/libexec/nginx/ngx_mail_module.so;
load_module /usr/local/libexec/nginx/ngx_stream_module.so;

#user  nobody;
worker_processes  1;

error_log  /var/log/nginx/error.log;
#

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
      proxy_cache_path /var/db/nginx      levels=1:2    keys_zone=STATIC:10m
      inactive=24h  max_size=1g;

## Hiawatha backend for mydomain.co.uk ##
upstream domain1  {
      server 10.30.1.14:80;
}

## Start mydomain.co.uk HTTP ##
server {
    listen       10.30.1.11:80;
    server_name  mydomain.co.uk *.mydomain.co.uk;
    return 301 https://$host$request_uri; ###FORCE HTTPS

    root   /usr/local/www/nginx;
    index  index.php index.html index.htm;

    ## send request back to domain1 ##
    location / {
     proxy_pass  http://domain1;
     proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
     proxy_redirect off;
     proxy_buffering off;
     proxy_set_header        Host            $host;
     proxy_set_header        X-Real-IP       $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_cache            STATIC;
     proxy_cache_valid      200  1d;
     proxy_cache_use_stale  error timeout invalid_header updating
                            http_500 http_502 http_503 http_504;
   }
}
## End  mydomain.co.uk HTTP ##

## Start mydomain.co.uk HTTPS##
server {
    listen       10.30.1.11:303 ssl;
    server_name  mydomain.co.uk *.mydomain.co.uk;
    ssl_certificate /etc/ssl/mydomain.co.uk.crt;
    ssl_certificate_key /etc/ssl/mydomain.co.uk.key;
    #ssl_client_certificate /etc/ssl/server/mydomain.co.uk.crt;
    ssl_verify_client      off;

    root   /usr/local/www/nginx;
    index  index.php index.html index.htm;

    ## send request back to domain1 ##
    location / {
     proxy_pass  http://domain1;
     proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
     proxy_redirect off;
     proxy_buffering off;
     proxy_set_header        Host            $host;
     proxy_set_header        X-Real-IP       $remote_addr;
     proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_cache            STATIC;
     proxy_cache_valid      200  1d;
     proxy_cache_use_stale  error timeout invalid_header updating
                            http_500 http_502 http_503 http_504;
            }
    }

}
```


----------



## SirDice (Apr 25, 2017)

I suggest not using nginx for this but instead use net/haproxy. Using nginx for reverse proxying is just horrid.

https://thehftguy.com/2016/10/03/haproxy-vs-nginx-why-you-should-never-use-nginx-for-load-balancing/

I have a working setup with HAproxy and Letsencrypt. HAproxy catches the challenge/response and redirects it to a local nginx that serves only the challenge/response. There are also some LUA scripts for HAProxy to deal with it on HAProxy itself but a local nginx works and doesn't require any weird scripting.


----------



## fred974 (Apr 25, 2017)

SirDice ,
All my website are ssl only...
Will net/haproxy do the job? In redirecting traffic to ssl?


----------



## drhowarddrfine (Apr 25, 2017)

Before I got it working, long ago, I had problems with letsencrypt, too, and every time I had to renew the certs, something wouldn't work right but letsencrypt was in a state of flux then.

You might look into certbot for FreeBSD which has worked well every time and it's all we use.

One potential problem is that you have a domain name but it's for your local server. Can letsencrypt see and get to that for verification? What error does nginx give you when you try to restart since you said it won't restart?

You can't redirect the http part for letsencrypt because it will use http for verification.


----------



## gkontos (Apr 25, 2017)

SirDice said:


> I suggest not using nginx for this but instead use net/haproxy. Using nginx for reverse proxying is just horrid.
> 
> https://thehftguy.com/2016/10/03/haproxy-vs-nginx-why-you-should-never-use-nginx-for-load-balancing/
> 
> I have a working setup with HAproxy and Letsencrypt. HAproxy catches the challenge/response and redirects it to a local nginx that serves only the challenge/response. There are also some LUA scripts for HAProxy to deal with it on HAProxy itself but a local nginx works and doesn't require any weird scripting.



nginx works fine for reverse proxying. Don't confuse it with load balancing.


----------



## fred974 (Apr 25, 2017)

ok I managed to get nginx to start by adding the above code inside a `server{}` block
Could someone please confirm if this is the correct way of doing it?


----------



## SirDice (Apr 25, 2017)

fred974 said:


> Will net/haproxy do the job? In redirecting traffic to ssl?


Yes, it will do the job. SSL can be terminated on HAProxy and you can configure it so HTTP will be automatically redirected to HTTPS.


----------



## SirDice (Apr 25, 2017)

gkontos said:


> nginx works fine for reverse proxying.


Sure. But you don't have a lot of metrics. And to be honest, just because it can doesn't mean you should. HAProxy is built from the ground up for reverse proxy/load-balancing.


----------



## gkontos (Apr 25, 2017)

SirDice said:


> Sure. But you don't have a lot of metrics. And to be honest, just because it can doesn't mean you should. HAProxy is built from the ground up for reverse proxy/load-balancing.


I know about the metrics. However, NGINX can also do some descent caching. HAPROXY is excellent for TCP based load balancing. Anyway, opinions ....


----------



## fred974 (Apr 25, 2017)

SirDice as curiousity, could you show what the config look like if you got one?


----------



## SirDice (Apr 25, 2017)

Sure, it's fairly up to date and I get an A+ rating on SSL.


```
global
        maxconn 30000
        daemon

        log /dev/log local2

        user nobody
        group nobody
       
        stats socket /var/run/haproxy.socket mode 777 level admin

        tune.ssl.default-dh-param 2048

        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES
:RSA+3DES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3 no-tls-tickets

defaults
        log global
        option httplog
        option dontlognull
        mode http
       
        option httpclose
        option abortonclose
        option forwardfor header X-Real-IP
        option http-server-close

        timeout connect 5000
        timeout client 50000
        timeout server 50000

        errorfile 400 /usr/local/www/haproxy/errors/400.http
        errorfile 403 /usr/local/www/haproxy/errors/403.http
        errorfile 404 /usr/local/www/haproxy/errors/404.http
        errorfile 500 /usr/local/www/haproxy/errors/500.http
        errorfile 503 /usr/local/www/haproxy/errors/503.http

        stats enable
        stats uri /haproxy?stats
        stats realm Statistics
        stats auth admin:supersecret
       
frontend http-in
        bind 1.2.3.4:80

        reqidel ^X-Real-IP:.*

        default_backend local

        # Letsencrypt
        acl is_letsencrypt path_beg /.well-known/acme-challenge/
        acl is_mail hdr_dom(host) -i mail.example.com
        acl is_webtrees hdr_dom(host) -i webtrees.example.com
       
        redirect scheme https if is_mail !{ ssl_fc }

        use_backend local if is_letsencrypt
        use_backend mail if is_mail
        use_backend webtrees if is_webtrees

frontend https-in
        bind 1.2.3.4:443 ssl crt /usr/local/etc/ssl/acme/example.com/haproxy.pem

        http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
        http-response set-header X-Frame-Options DENY
        http-response set-header X-Content-Type-Options nosniff

        tcp-request content accept if { req_ssl_hello_type 1 }
        reqidel ^X-Real-IP:.*

        default_backend local

        # Letsencrypt
        acl is_letsencrypt path_beg /.well-known/acme-challenge/
        acl is_mail hdr_dom(host) -i mail.example.com
        acl is_webtrees hdr_dom(host) -i webtrees.example.com

        use_backend local if is_letsencrypt
        use_backend mail if is_mail
        use_backend webtrees if is_webtrees

backend local
        option httpchk GET /up.txt
        server localhost 127.0.0.1:80 check

backend webtrees
        option httpchk GET /up.txt
        server webtrees 192.168.21.3:80 check

#backend dayz
        #option httpchk GET /up.txt
        #server dayz 192.168.21.20:80 check

backend mail
        option httpchk GET /up.txt
        server mail 192.168.21.4:80 check
```

I removed the IPv6 bits, changed the IP addresses and used example.com. The rest should be fairly self-explanatory. I have this running on a VPS on the host, the various backends are running inside jails. The big advantage of this is that I can run multiple websites on different webservers, all on the same, single, external IP address.


----------



## fred974 (Apr 25, 2017)

SirDice said:


> I removed the IPv6 bits, changed the IP addresses and used example.com. The rest should be fairly self-explanatory


..Ok I can see how this work, one question..How did you integret the letsencrypt part?
How that part is setup?


----------



## SirDice (Apr 26, 2017)

The important bits are here:

```
acl is_letsencrypt path_beg /.well-known/acme-challenge/

use_backend local if is_letsencrypt

backend local
        option httpchk GET /up.txt
        server localhost 127.0.0.1:80 check
```
This directs any request for /.well-known/acme-challenge/ to a local webserver. This is just a plain nginx, the important part:

```
location /.well-known/acme-challenge {
                alias /usr/local/www/acme;
                autoindex off;
        }
```

I then modified /usr/local/etc/acme/acme-client.sh slightly:

```
#!/bin/sh -e

BASEDIR="/usr/local/etc/acme"
SSLDIR="/usr/local/etc/ssl/acme"
DOMAINSFILE="${BASEDIR}/domains.txt"
CHALLENGEDIR="/usr/local/www/acme"

[ ! -d "${SSLDIR}/private" ] && mkdir -pm700 "${SSLDIR}/private"

cat "${DOMAINSFILE}" | while read domain line ; do
   CERTSDIR="${SSLDIR}/${domain}"
   [ ! -d "${CERTSDIR}" ] && mkdir -pm755 "${CERTSDIR}"
   set +e # RC=2 when time to expire > 30 days
   acme-client -b -C "${CHALLENGEDIR}" \
               -k "${SSLDIR}/private/${domain}.pem" \
               -c "${CERTSDIR}" \
               ${domain} ${line}
   RC=$?
   set -e
   [ $RC -ne 0 -a $RC -ne 2 ] && exit $RC
done
```
That way it saves the challenge/response to /usr/local/www/acme/ which is served by the local nginx. I still need to tweak the deploy.sh script though. But the idea is to use the periodic(8) scripts, /etc/periodic.conf:

```
weekly_acme_client_enable="YES"
weekly_acme_client_renewscript="/usr/local/etc/acme/acme-client.sh"
weekly_acme_client_deployscript="/usr/local/etc/acme/deploy.sh"
```

The acme-client.sh script reads from domains.txt a list of domains to check, and because HAProxy always catches the /.well-known/acme-challenge it's all handled automatically.


----------

