Difference between revisions of "Setting up HTTPS using Let's Encrypt"

From EPrints Documentation
Jump to: navigation, search
m
(Additional Notes: Add some more notes to determine if you need rewrite trigger.)
 
(19 intermediate revisions by 3 users not shown)
Line 1: Line 1:
By default EPrints does not come setup to use HTTPS.  This is because you need to generate an SSL certificate and if this is not signed by a known Certificate Authority a user web browser is likely to complain vigourously.  In the past it has typically costed a fair amount of money to obtain an SSL certificate from your hosting provider.  However, now there is [https://letsencrypt.org/ Let's Encrypt] it is possible to get a certificate for free.  This is not to say you should use Let's Encrypt in all cases.  UK academic institutions should continue to obtain their SSL certificates through Jisc and other institutions/organisations may already have similar arrangements for obtaining SSL certificates.
+
{{Manual}}
 +
By default EPrints does not come setup to use HTTPS.  This is because you need to generate a SSL certificate and if this is not signed by a known Certificate Authority a user's web browser is likely to complain vigorously.  In the past it has typically costed a fair amount of money to obtain a SSL certificate from your hosting provider.  However, now there is [https://letsencrypt.org/ Let's Encrypt] it is possible to get a certificate for free.  This is not to say you should use Let's Encrypt in all cases.  UK academic institutions should continue to obtain their SSL certificates through Jisc and other institutions/organisations may already have similar arrangements for obtaining SSL certificates.
  
As well as being free, Let's Encrypt has an automated way for renewing certificates.  However, it should be noted that the length of its certificates is a lot shorter than usually at 3 months.  As the renewal process is automatic this time period is more appropriate to maximise security.  Let's Encrypt's certificate generation requires your EPrints repository to be publicly accessible.  Beneath are instructions on how to setup EPrints to enable HTTPS with a Let's Encrypt certificate.
+
As well as being free, Let's Encrypt has an automated way for renewing certificates.  However, it should be noted that the length of its certificates is a lot shorter at just 3 months.  As the renewal process is automatic, this time period is more appropriate to maximise security.  Let's Encrypt's certificate generation requires your EPrints repository to be publicly accessible.  Beneath are instructions on how to set up EPrints to enable HTTPS with a Let's Encrypt certificate.
  
 
1. Go to the https://certbot.eff.org/ and follow the instructions for your web server and operating system (the former will almost always be Apache) and follow the instructions provided.
 
1. Go to the https://certbot.eff.org/ and follow the instructions for your web server and operating system (the former will almost always be Apache) and follow the instructions provided.
 
  
 
2. When you get to the point of running the certbot command you want the certificate only so should type as the root user:
 
2. When you get to the point of running the certbot command you want the certificate only so should type as the root user:
  
certbot --apache certonly
+
<syntaxhighlight lang="bash">
 +
certbot --apache certonly
 +
</syntaxhighlight>
  
 
3. Take a note of the path to were your certificate, key and CA chain have been saved.  This will probably be something like
 
3. Take a note of the path to were your certificate, key and CA chain have been saved.  This will probably be something like
 
/etc/letsencrypt/live/example.eprints.org/
 
  
4. If you have not already done so create an directory called '''ssl''' inside your archive's directory. E.g.
+
<syntaxhighlight lang="bash">
 +
/etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/
 +
</syntaxhighlight>
 +
 
 +
4. If you have not already done so, create an directory called <tt>ssl</tt> inside your archive's directory, i.e.
 +
 
 +
<syntaxhighlight lang="bash">
 +
mkdir EPRINTS_PATH/archives/REPOID/ssl/
 +
</syntaxhighlight>
  
mkdir /opt/eprints3/archives/example/ssl/
+
5. Create a file inside the new ssl directory called <tt>securevhost.conf</tt> and include the following contents, amending the <tt>ServerName</tt>, <tt>SSLCertificateFile</tt>, <tt>SSLCertificateKeyFile</tt> <tt>SSLCertificateChainFile</tt> and <tt>Include</tt> as appropriate at least.  You will note that this configuration also enables [https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security HTTP Strict Transport Security (HSTS)] for additional security.  This ensures that after the first time you visit HTTPS for a particular site in your web browser, all future HTTP requests for this hostname will automatically be converted by your browser to HTTPS.
  
5. Create a file inside the new ssl directory called '''securevhost.conf''' and include the following contents, amending the '''ServerName''', '''SSLCertificateFile''', '''SSLCertificateKeyFile''' '''SSLCertificateChainFile''' and '''Include''' as appropriate.  You will note that this configuration also enables [https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security HTTP Strict Transport Security (HSTS)] for additional security.  This ensures that after the first time you visit HTTPS for a particular site in your web browser, all future HTTP requests for this hostname will automatically be converted by your browser to HTTPS.
+
<syntaxhighlight lang="apache">
 +
<VirtualHost *:443>
  
<VirtualHost *:443>
+
     ServerName YOUR-REPOSITORY-DOMAIN:443
 
     ServerName example.eprints.org:443
 
 
   
 
   
 
     # Enable HSTS
 
     # Enable HSTS
Line 28: Line 35:
 
   
 
   
 
     SSLEngine on
 
     SSLEngine on
     SSLProtocol all -SSLv2 -SSLv3
+
     SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
 
     SSLHonorCipherOrder on
 
     SSLHonorCipherOrder on
     SSLCipherSuite HIGH:!aNULL:!eNULL:!kECDH:!aDH:!RC4:!3DES:!CAMELLIA:!MD5:!PSK:!SRP:!KRB5:@STRENGTH
+
     SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
+
 
     SSLCertificateFile /etc/letsencrypt/live/example.eprints.org/cert.pem
+
     SSLCertificateFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/cert.pem
     SSLCertificateKeyFile /etc/letsencrypt/live/example.eprints.org/privkey.pem
+
     SSLCertificateKeyFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/privkey.pem
     SSLCertificateChainFile /etc/letsencrypt/live/example.eprints.org/chain.pem
+
     SSLCertificateChainFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/chain.pem
 
   
 
   
 
     SetEnvIf User-Agent ".*MSIE.*" \
 
     SetEnvIf User-Agent ".*MSIE.*" \
Line 46: Line 53:
 
         "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
 
         "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
 
   
 
   
     Include /opt/eprints3/cfg/apache_ssl/test.conf
+
     Include EPRINTS_PATH/cfg/apache_ssl/REPOID.conf
 
   
 
   
 
     PerlTransHandler +EPrints::Apache::Rewrite
 
     PerlTransHandler +EPrints::Apache::Rewrite
 
   
 
   
  </VirtualHost>
+
</VirtualHost>
 +
</syntaxhighlight>
 +
 
 +
6. If you have not already done so, add a configuration file at <tt>/opt/eprints3/archives/REPOID/cfg/cfg.d/15_https.pl</tt> with the following configuration:
 +
 
 +
<syntaxhighlight lang="perl">
 +
$c->{securehost} = $c->{host};
 +
$c->{secureport} = 443;
 +
  $c->{http_root} = undef;
 +
</syntaxhighlight>
 +
 
 +
7. Now you will need to regenerate the rest of your Apache configuration for EPrints with the following command run as the EPrints user. (Substitute the path as appropriate):
 +
 
 +
<syntaxhighlight lang="bash">
 +
/opt/eprints3/bin/generate_apacheconf --system --replace
 +
</syntaxhighlight>
 +
 
 +
8. Apache will not yet have been setup to include the Apache configuration file you created in step 5.  To include this, you will need to edit the file in your Apache configuration directory (<tt>/etc/httpd/</tt> on RHEL/CentOS/Fedora and <tt>/etc/apache2/</tt> on Debian/Ubuntu) and find the file that contains the line <tt>cfg/apache.conf</tt>, e.g.
 +
 
 +
<syntaxhighlight lang="bash">
 +
cd /etc/httpd/
 +
grep -r "cfg/apache.conf" *
 +
</syntaxhighlight>
 +
 
 +
9. Once you have found it you will need to edit it and add the following line, changing the path as appropriate:
 +
 
 +
<syntaxhighlight lang="apache">
 +
Include EPRINTS_PATH/archives/REPOID/ssl/securevhost.conf
 +
</syntaxhighlight>
  
6. Now you will need to regenerate the rest of your Apache configuration for EPrints will the following command run as the EPrints user. (Substitute the path as appropriate):
+
10. Now, (as root or using sudo) test that the Apache configuration is correct and fix any issues if necessary:
  
/opt/eprints3/bin/generate_apacheconf --system --replace
+
<syntaxhighlight lang="bash">
 +
apachectl configtest
 +
</syntaxhighlight>
  
7. Apache will not yet have been setup to include the Apache configuration file you create in step 5.  To include this you will need to edit the file in your Apache configuration directory (/etc/httpd/ on RHEL/CentOS/Fedora and /etc/apache2/ on Debian/Ubuntu) and find the file that contains the line '''cfg/apache.conf'''. E.g.
+
11. Restart Apache and check whether you can access your EPrints repository using HTTPS in a web browser:
  
cd /etc/httpd/
+
<syntaxhighlight lang="bash">
grep "cfg/apache.conf" *
+
apachectl restart
 +
</syntaxhighlight>
  
8. Once you have found it you will need to edit it and add the following line, changing the path as appropriate:
+
12. To ensure that the certificate get renewed when necessary enable the certbot-renew timer:
 +
<syntaxhighlight lang="bash">
 +
systemctl enable --now certbot-renew.timer
 +
</syntaxhighlight>
  
  Include /opt/eprints3/archives/example/ssl/securevhost.conf
+
== Additional Notes ==
 +
The default EPrints Apache Rewrite handler declines any requests to a URL containing <code>/.</code>. However, this should not mean that the challenge/response for a Let's Encrypt certificate renewal that uses the <code>/.well-known/acme-challenge/</code> path will fail.  As if a request is declined by the handler, it should drop through to looking for the file on the <code>DocumentRoot</code> either set for the Apache or a specific VirtualHost.
  
9. Now test that the Apache configuration is correct and fix any issues if necessary:
+
You can test to confirm this by creating a <code>.well-known/acme-challenge/</code> directory in your <code>DocumentRoot</code>.  Typically, this will be something like <code>/var/www/html/.well-known/acme-challenge/</code>.  If you then add a small text file you can request (e.g. <code>test.txt</code>) to this directory and then see if you can download it from your web browser, (e.g. <code>http://example.eprints.org/.well-known/acme-challenge/test.txt</code>), if you can download this, Let's Encrypt Acme challenges should work and you can remove the <code>.well-known</code> directory as Certbot will re-create this when it needs it.
  
apachectl configtest
+
However, if this does not work, you could make this work you could add a location block to your Apache config, to ensure the <code>/.well-known/</code> are not handled by EPrints, or use an EPrints URL rewrite trigger to handle the request:
  
10. Restart Apache and check whether you can access your EPrints repository using HTTPS in a web browser:
+
<syntaxhighlight lang="perl">
 +
#this could be saved into e.g. [EPRINTS_ROOT]/lib/cfg.d/z_rewrite_url_LetsEncrypt.pl
  
apachectl restart
+
# import Apache constant e.g. 'OK'
 +
use EPrints::Apache::AnApache;
  
11. If the guide on cerbot.eff.org you used in step 1 tells you to to setup a cron job then follow those instructions.  The easiest way to do this is probably to run ''''crontab -e''' as root and add the following cron job, substituting the minute (43) and hours (6,18) for you own choice maintaining a 12 hour gap between the hours:
+
$c->add_trigger( EP_TRIGGER_URL_REWRITE, sub {
 +
        my( %o ) = @_;
  
43 6,18 * * * certbot renew
+
        # if the incoming request matches the LetsEncrypt challenge URL...
 +
        if( $o{uri} =~ m!^${o{urlpath}}/\.well-known/acme-challenge/! )
 +
        {
 +
                # and the LetsEncrypt renewal process has put the file in EPRINTS_ROOT/archives/ARCHIVEID/html/.well-known/acme-challenge/
 +
                if( -f $o{repository}->get_conf( "htdocs_path" ).$o{uri} )
 +
                {
 +
                        # reply with an HTTP '200'
 +
                        ${$o{return_code}} = OK;
 +
                        # and return the file reqested
 +
                        $o{request}->filename( $o{repository}->get_conf( "htdocs_path" ).$o{uri} );
 +
                }
 +
                # and say that we've handled the request - so no more triggers should be run.
 +
                return EP_TRIGGER_DONE;
 +
        }
 +
} );
 +
</syntaxhighlight>

Latest revision as of 00:02, 29 March 2023

Manual Sections

By default EPrints does not come setup to use HTTPS. This is because you need to generate a SSL certificate and if this is not signed by a known Certificate Authority a user's web browser is likely to complain vigorously. In the past it has typically costed a fair amount of money to obtain a SSL certificate from your hosting provider. However, now there is Let's Encrypt it is possible to get a certificate for free. This is not to say you should use Let's Encrypt in all cases. UK academic institutions should continue to obtain their SSL certificates through Jisc and other institutions/organisations may already have similar arrangements for obtaining SSL certificates.

As well as being free, Let's Encrypt has an automated way for renewing certificates. However, it should be noted that the length of its certificates is a lot shorter at just 3 months. As the renewal process is automatic, this time period is more appropriate to maximise security. Let's Encrypt's certificate generation requires your EPrints repository to be publicly accessible. Beneath are instructions on how to set up EPrints to enable HTTPS with a Let's Encrypt certificate.

1. Go to the https://certbot.eff.org/ and follow the instructions for your web server and operating system (the former will almost always be Apache) and follow the instructions provided.

2. When you get to the point of running the certbot command you want the certificate only so should type as the root user:

certbot --apache certonly

3. Take a note of the path to were your certificate, key and CA chain have been saved. This will probably be something like

 
/etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/

4. If you have not already done so, create an directory called ssl inside your archive's directory, i.e.

mkdir EPRINTS_PATH/archives/REPOID/ssl/

5. Create a file inside the new ssl directory called securevhost.conf and include the following contents, amending the ServerName, SSLCertificateFile, SSLCertificateKeyFile SSLCertificateChainFile and Include as appropriate at least. You will note that this configuration also enables HTTP Strict Transport Security (HSTS) for additional security. This ensures that after the first time you visit HTTPS for a particular site in your web browser, all future HTTP requests for this hostname will automatically be converted by your browser to HTTPS.

<VirtualHost *:443>

     ServerName YOUR-REPOSITORY-DOMAIN:443
 
     # Enable HSTS
     Header always set Strict-Transport-Security "max-age=63072000;"
 
     SSLEngine on
     SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
     SSLHonorCipherOrder on
     SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256

     SSLCertificateFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/cert.pem
     SSLCertificateKeyFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/privkey.pem
     SSLCertificateChainFile /etc/letsencrypt/live/YOUR-REPOSITORY-DOMAIN/chain.pem
 
     SetEnvIf User-Agent ".*MSIE.*" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
 
     LogLevel warn
     ErrorLog logs/ssl_error_log    
     TransferLog logs/ssl_access_log
     CustomLog logs/ssl_request_log \
         "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
 
     Include EPRINTS_PATH/cfg/apache_ssl/REPOID.conf
 
     PerlTransHandler +EPrints::Apache::Rewrite
 
</VirtualHost>

6. If you have not already done so, add a configuration file at /opt/eprints3/archives/REPOID/cfg/cfg.d/15_https.pl with the following configuration:

 $c->{securehost} = $c->{host};
 $c->{secureport} = 443;
 $c->{http_root} = undef;

7. Now you will need to regenerate the rest of your Apache configuration for EPrints with the following command run as the EPrints user. (Substitute the path as appropriate):

/opt/eprints3/bin/generate_apacheconf --system --replace

8. Apache will not yet have been setup to include the Apache configuration file you created in step 5. To include this, you will need to edit the file in your Apache configuration directory (/etc/httpd/ on RHEL/CentOS/Fedora and /etc/apache2/ on Debian/Ubuntu) and find the file that contains the line cfg/apache.conf, e.g.

cd /etc/httpd/
grep -r "cfg/apache.conf" *

9. Once you have found it you will need to edit it and add the following line, changing the path as appropriate:

Include EPRINTS_PATH/archives/REPOID/ssl/securevhost.conf

10. Now, (as root or using sudo) test that the Apache configuration is correct and fix any issues if necessary:

apachectl configtest

11. Restart Apache and check whether you can access your EPrints repository using HTTPS in a web browser:

apachectl restart

12. To ensure that the certificate get renewed when necessary enable the certbot-renew timer:

systemctl enable --now certbot-renew.timer

Additional Notes

The default EPrints Apache Rewrite handler declines any requests to a URL containing /.. However, this should not mean that the challenge/response for a Let's Encrypt certificate renewal that uses the /.well-known/acme-challenge/ path will fail. As if a request is declined by the handler, it should drop through to looking for the file on the DocumentRoot either set for the Apache or a specific VirtualHost.

You can test to confirm this by creating a .well-known/acme-challenge/ directory in your DocumentRoot. Typically, this will be something like /var/www/html/.well-known/acme-challenge/. If you then add a small text file you can request (e.g. test.txt) to this directory and then see if you can download it from your web browser, (e.g. http://example.eprints.org/.well-known/acme-challenge/test.txt), if you can download this, Let's Encrypt Acme challenges should work and you can remove the .well-known directory as Certbot will re-create this when it needs it.

However, if this does not work, you could make this work you could add a location block to your Apache config, to ensure the /.well-known/ are not handled by EPrints, or use an EPrints URL rewrite trigger to handle the request:

#this could be saved into e.g. [EPRINTS_ROOT]/lib/cfg.d/z_rewrite_url_LetsEncrypt.pl

# import Apache constant e.g. 'OK'
use EPrints::Apache::AnApache;

$c->add_trigger( EP_TRIGGER_URL_REWRITE, sub {
        my( %o ) = @_;

        # if the incoming request matches the LetsEncrypt challenge URL...
        if( $o{uri} =~ m!^${o{urlpath}}/\.well-known/acme-challenge/! )
        {
                # and the LetsEncrypt renewal process has put the file in EPRINTS_ROOT/archives/ARCHIVEID/html/.well-known/acme-challenge/
                if( -f $o{repository}->get_conf( "htdocs_path" ).$o{uri} )
                {
                        # reply with an HTTP '200' 
                        ${$o{return_code}} = OK;
                        # and return the file reqested
                        $o{request}->filename( $o{repository}->get_conf( "htdocs_path" ).$o{uri} );
                }
                # and say that we've handled the request - so no more triggers should be run.
                return EP_TRIGGER_DONE;
        }
} );