RESTful Server Administration (Ubuntu 16.04)¶
- Using openssl version: OpenSSL 1.0.2g 1 Mar 2016, the private key generation method follows:
root@gdp-rest-01$ openssl req -nodes -newkey rsa:2048 -keyout gdp-rest-01.eecs.berkeley.edu.key -out gdp-rest-01.eecs.berkeley.edu.csr Generating a 2048 bit RSA private key ..........................................................................+++ .................................................................................................................+++ writing new private key to 'gdp-rest-01.eecs.berkeley.edu.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:US State or Province Name (full name) [Some-State]:California Locality Name (eg, city) []:Berkeley Organization Name (eg, company) [Internet Widgits Pty Ltd]:UC Berkeley Organizational Unit Name (eg, section) []:Swarm Lab Common Name (e.g. server FQDN or YOUR name) []:gdp-rest-01.eecs.berkeley.edu Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: root@gdp-rest-01$
- Create a passphrase-protected copy of the private key and copy this "des3" file to a secure storage location as a backup for the unprotected private key:
openssl rsa -des3 -in gdp-rest-01.eecs.berkeley.edu.key -out $USER.des3.gdp-rest-01.eecs.berkeley.edu.key
Do not delete other $USER.des3.* files found in the root user's home directory. :)
The above csr was submitted via https://iris.eecs.berkeley.edu/forms/cert-request.html,
which returned a signed certificate via email the next day.It is a 3 year certificate
Installing the certificates into the lighttpd web server requires the manual construction of
a pem file, which is assembled from the private key certificate and the InCommon certificates.
The "Format(s) most suitable for your server software" InCommon cer file should be downloaded
as it will be placed in the /etc/lighttpd/ssl/ directory along with the constructed pem file, but it will
not be used to construct the pem file (due to certificate order issues which prevent lighttpd from running).
The pem file content from top to bottom is required to be (1) our private key, (2) our certificate
from InCommon, (3) lowest intermediate InCommon certificate, then other intermediate
certificates up the hierarchy, ending with the highest certificate. The individual certificates
can be downloaded from InCommon to facilitate construction of the pem file in the desired
order, noting that the *interim_only.cer InCommon certificate download currently contains 3
certificates which are already in a compatible order. Our key can be concatenated with
the following two InCommon files (downloaded from their website) to create a working pem
file:
cat gdp-rest-01.eecs.berkeley.edu.key \ gdp-rest-01_eecs_berkeley_edu_cert_only.cer \ gdp-rest-01_eecs_berkeley_edu_interm_only.cer \ > gdp-rest-01.eecs.berkeley.edu.pem
- Place the .pem and .cer files in /etc/lighttpd/ssl/ with appropriate permissions and ownership, noting that lighttpd reads these at startup while it is still has root privilege:
/etc/lighttpd/ssl: total used in directory 28 available 43146256 dr-x------ 2 root root 4096 May 16 15:50 . drwxr-xr-x 5 root root 4096 May 16 15:34 .. -r-------- 1 root root 7663 May 16 15:42 gdp-rest-01_eecs_berkeley_edu.cer -r-------- 1 root root 9368 May 16 11:58 gdp-rest-01.eecs.berkeley.edu.pem
- Edit (may need to create) /etc/lighttpd/conf-available/10-scgi.conf:
server.modules += ( "mod_scgi" ) scgi.server = ( # stable REST service "/gdp/v1/" => ( "gdp" => ( "host" => "127.0.0.1", "port" => 8001, "check-local" => "disable", ) ), # development REST service "/gdp/v2/" => ( "gdp" => ( "host" => "127.0.0.1", "port" => 8002, "check-local" => "disable", ) ), )
- Edit /etc/lighttpd/conf-available/10-ssl.conf:
# /usr/share/doc/lighttpd/ssl.txt # disable entire default ubuntu block # # $SERVER["socket"] == "0.0.0.0:443" { # ssl.engine = "enable" # ssl.pemfile = "/etc/lighttpd/server.pem" # ssl.cipher-list = "ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!EDH:!AESGCM" # ssl.honor-cipher-order = "enable" # } # gdp-rest-01 HTTPS settings below must be paired with HTTPS-only lighttpd.conf # # https://mozilla.github.io/server-side-tls/ssl-config-generator/ # # lighttpd 1.4.37 | modern profile | OpenSSL 1.0.2g # # Oldest compatible clients : Firefox 27, Chrome 30, IE 11 on Windows 7, # Edge, Opera 17, Safari 9, Android 5.0, and Java 8 # # cipher-list further customized (possibly redundant) to exclude the following: # # :!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH # :!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA # # see lighttpd.conf server.port as this is an HTTPS-only installation #$SERVER["socket"] == ":443" { protocol = "https://" ssl.engine = "enable" ssl.disable-client-renegotiation = "enable" # pemfile is cert+privkey, ca-file is the intermediate chain in one file #ssl.pemfile = "/path/to/signed_cert_plus_private_key.pem" #ssl.ca-file = "/path/to/intermediate_certificate.pem" ssl.pemfile = "/etc/lighttpd/ssl/gdp-rest-01.eecs.berkeley.edu.pem" ssl.ca-file = "/etc/lighttpd/ssl/gdp-rest-01_eecs_berkeley_edu.cer" server.name = "gdp-rest-01.eecs.berkeley.edu" # see lighttpd.conf, this is a duplicate when https-only from main conf # server.document-root = "/var/www/html" # logjam prevention, generate the following file in /etc/lighttpd/ssl/, # then chmod 400 with root:root ownership: # openssl dhparam -out dhparams.pem 2048 ssl.dh-file = "/etc/lighttpd/ssl/dhparams.pem" # ECDH/ECDHE ciphers curve strength (see `openssl ecparam -list_curves`) ssl.ec-curve = "secp384r1" # Compression is by default off at compile-time, but use if needed ssl.use-compression = "disable" # Environment flag for HTTPS enabled setenv.add-environment = ( "HTTPS" => "on" ) # modern configuration, tweak to your needs ssl.use-sslv2 = "disable" ssl.use-sslv3 = "disable" ssl.honor-cipher-order = "enable" ssl.cipher-list = "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:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA" # HSTS(15768000 seconds = 6 months) setenv.add-response-header = ( "Strict-Transport-Security" => "max-age=15768000;" ) # see lighttpd.conf server.port as this is an HTTPS-only installation #}
Follow instructions listed in above 10-ssl.conf to generate dhparams.pem file in the /etc/lighttpd/ssl directory.
Edit /etc/lighttpd/lighttpd.conf:
# gdp-rest-01 is an HTTPS-only server with basic client authentication and # CORS wildcard support. # # Signpost Project Notes: # # Signpost cannot do more than basic client authentication, so sensor # data privacy is obtained by only supporting HTTPS sessions. Basic # client authentication is used mostly to diminish Internet probing # of the GDP through this Internet accessible gateway, however it # may become important to protect the data of specific accounts in # the future, so it seemed reasonable to begin with it enabled. # # EC Demo Project Notes: # # Standard web browsers do not permit Javascript communication, other # than with the Javascript's origin host. PTII and other Javascript # accessors therefore cannot interact with non-origin servers # including this GDP RESTful gateway by default. Web browsers can # negotiate a waiver of (1) with a web server (via CORS), by # submitting a pre-flight OPTIONS method request. A complication # arises because the web server is configured to require basic # authentication, but authentication explicitly disallows a wildcard # Access-Control-Allow-Origin header. To work around the # incompatibility of CORS wildcard rules and client authentication, # this configuration permits pre-flight OPTIONS requests without # client authentication (without leaking/verifying any information and # without any ability to change data on the server), while continuing # to enforce client authentication on GDP requests (e.g., GET, POST, # PUT). The gdp-rest binary scgi-backend has been modified to accept # pre-flight OPTIONS requests and send a success response, so the web # browser will submit the real request along with authentication # credentials. # # Tested with Firefox 54/55, Chrome 60, Safari 10. # server.modules = ( "mod_access", "mod_alias", "mod_compress", "mod_redirect", # "mod_rewrite", "mod_setenv", "mod_auth", # "mod_status", ) auth.backend = "plain" auth.backend.plain.userfile = "/etc/lighttpd/.plainauth" $HTTP["request-method"] != "OPTIONS" { # non-OPTIONS method reqs aimed at GDP GCLs require client authentication auth.require = ( "/gdp/" => ( "method" => "basic", "realm" => "gdp-rest-01.eecs.berkeley.edu", "require" => "valid-user", ) ) # web browsers require this header injection, even after CORS pre-flight setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*", ) } $HTTP["request-method"] == "OPTIONS" { # CORS pre-flight header injections setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*", "Access-Control-Allow-Headers" => "Authorization, Content-Type", "Access-Control-Allow-Methods" => "GET, POST, PUT, OPTIONS", "Access-Control-Max-Age" => "86400", ) } server.document-root = "/var/www/html" server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) server.errorlog = "/var/log/lighttpd/error.log" server.pid-file = "/var/run/lighttpd.pid" server.username = "www-data" server.groupname = "www-data" # gdp-rest-01 is HTTPS-only # additional settings made by /etc/lighttpd/conf-enabled/10-ssl.conf # server.port = 8080 server.port = 443 # handy lighttpd debug knobs # debug.log-request-header = "enable" # debug.log-file-not-found = "enable" # debug.log-request-handling = "enable" index-file.names = ( "index.php", "index.html", "index.lighttpd.html" ) url.access-deny = ( "~", ".inc" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) compress.cache-dir = "/var/cache/lighttpd/compress/" compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) # default listening port for IPv6 falls back to the IPv4 port ## Use ipv6 if available #include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/create-mime.assign.pl" include_shell "/usr/share/lighttpd/include-conf-enabled.pl" # deny !~ berkeley.edu | localhost (at least while under construction) #$HTTP["remoteip"] !~ "^(128\.32\.*|127\.0\.*)" { # url.access-deny = ( "" ) #} # disable directory browsing - all interesting traffic goes to scgi anyway $HTTP["url"] =~ "^/" { dir-listing.activate = "disable" }
Construct /etc/lighttpd/.plainauth with one or more (unencrypted, plaintext) sensor
project "username:password\n" lines, then apply chmod 600 and chown www-data:www-data,
which allows lighttpd to perform plaintext sensor client authentication within the HTTPS encrypted session.Use /usr/sbin/lighty-enable-mod and /usr/sbin/lighty-disable-mod to enable needed modules,
and disable extraneous modules:
/usr/sbin/lighty-enable-mod scgi /usr/sbin/lighty-enable-mod ssl /usr/sbin/lighty-disable-mod javascript-alias
- Check for configuration syntax errors:
lighttpd -t -f /etc/lighttpd/lighttpd.conf
- Launch lighttpd in interactive mode first, to check for issues (like certificate problems, which do not show in the error.log):
lighttpd -f /etc/lighttpd/lighttpd.conf -D
Point a web browser at the server (local, remote clients) and test.
http:// should be inaccessible, https:// should ask for a username and password (from the .plainauth file).If all is well, enable and start lighttpd:
systemctl enable lighttpd systemctl start lighttpd
If all is well and wider accessibility is required, tighten up access via iptables then remove the "remoteip" filter from lighttpd.conf.
gdp-rest-01 is currently using the following script to manage configuration changes to iptables:
#!/bin/bash -v # gdp-rest-01: ~root/FIREWALL__DO_NOT_DELETE/iptables.sh # # PRE-REQUISITE: apt install iptables-persistent # # Track changes (on boot iptables-persistent loads from /etc/iptables/rules.v4) cp -f /etc/iptables/rules.v4 \ /root/FIREWALL__DO_NOT_DELETE/rules.v4.previous # -F with no chain target flushes all chains from table iptables -t filter -F iptables -t nat -F # -X with no chain target flushes all user-defined chains from table, redundant? iptables -t filter -X iptables -t nat -X # -P sets default policy iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT DROP # <--> localhost/loopback iptables -A INPUT -i lo -j ACCEPT iptables -A OUTPUT -o lo -j ACCEPT # <!!> wifi disabled iptables -A INPUT -i wlp2so -j DROP iptables -A OUTPUT -o wlp2so -j DROP # net <--- host iptables -A OUTPUT -o enp0s25 -m state --state NEW,ESTABLISHED -j ACCEPT # net ---> host iptables -A INPUT -i enp0s25 -m state --state ESTABLISHED -j ACCEPT # UCB ---> host SSH NEW iptables -A INPUT -i enp0s25 -s 128.32.0.0/16 \ -m state --state NEW \ -p tcp --dport 22 -j ACCEPT # UCB VPN too iptables -A INPUT -i enp0s25 -s 136.152.0.0/16 \ -m state --state NEW \ -p tcp --dport 22 -j ACCEPT # net ---> host HTTPS NEW iptables -A INPUT -i enp0s25 \ -m state --state NEW \ -p tcp --dport 443 -j ACCEPT # If cut-off, ssh to a gdp router from which gdp-rest-01 is reachable # # <--> UCB network allowed by default iptables -A INPUT -i enp0s25 -s 128.32.0.0/16 -j ACCEPT iptables -A OUTPUT -o enp0s25 -s 128.32.0.0/16 -j ACCEPT # Monitoring: # watch netstat -a -W -p -t -u -e # and # watch iptables -L --line-numbers -n -v # # Identifying which binary is using a port: # # lsof -i :<port_or_portname> # # Track changes (on boot iptables-persistent loads from /etc/iptables/rules.v4) iptables-save > /etc/iptables/rules.v4 cp -f /etc/iptables/rules.v4 \ /root/FIREWALL__DO_NOT_DELETE/rules.v4.`date +%Y%m%d%H%M%S`