From 13ffcee31518e6aa1cc4f5aaf248268068755a5d Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 20 Apr 2017 18:04:57 -0600 Subject: [PATCH] move to own project --- .../LaunchDaemons/com.caddyserver.web.plist | 53 +++ README.md | 65 +++ etc/systemd/system/caddy.service | 62 +++ etc/tmpfiles.d/caddy.conf | 10 + install-caddy | 423 ++++++++++++++++++ 5 files changed, 613 insertions(+) create mode 100644 Library/LaunchDaemons/com.caddyserver.web.plist create mode 100644 README.md create mode 100644 etc/systemd/system/caddy.service create mode 100644 etc/tmpfiles.d/caddy.conf create mode 100755 install-caddy diff --git a/Library/LaunchDaemons/com.caddyserver.web.plist b/Library/LaunchDaemons/com.caddyserver.web.plist new file mode 100644 index 0000000..980bfb2 --- /dev/null +++ b/Library/LaunchDaemons/com.caddyserver.web.plist @@ -0,0 +1,53 @@ + + + + + Label + Caddy + ProgramArguments + + /usr/local/bin/caddy + -agree + -conf + /etc/caddy/Caddyfile + -root + /var/tmp + + EnvironmentVariables + + CADDYPATH + /etc/ssl/caddy + + + UserName + root + GroupName + wheel + InitGroups + + + RunAtLoad + + KeepAlive + + Crashed + + + + SoftResourceLimits + + NumberOfFiles + 8192 + + HardResourceLimits + + + WorkingDirectory + /etc/ssl/caddy + + StandardErrorPath + /var/log/caddy/error.log + StandardOutPath + /var/log/caddy/info.log + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..2ff714d --- /dev/null +++ b/README.md @@ -0,0 +1,65 @@ +Caddy Installer +--------------- + +Works on + + * Ubuntu Linux + * macOS Sierra + * probably lots of others + +Supports + + * systemd (Ubuntu, Fedora, Arch, etc) + * launchd (OS X, macOS, Darwin) + +Install +------- + +Yes... you install the installer + +```bash +curl -L https://git.daplie.com/Daplie/daplie-snippets/raw/master/caddy-installer/install-caddy -o install-caddy +sudo mv install-caddy /usr/local/bin/install-caddy +sudo chmod a+x /usr/local/bin/install-caddy +``` + +Usage +----- + +``` +install-caddy --help + +Usage: install-caddy [plugin1,plugin2,...] + +Optional arguments (defaults shown) + --os darwin # any of windows darwin linux freebsd openbsd + --arch amd64 # any of arm64 amd64 386 armv5 armv6l armv7l + +Features: +DNS,awslambda,cors,expires,filemanager,filter,git,hugo,ipfilter,jsonp,jwt,locale,mailout,minify,multipass,prometheus,ratelimit,realip,search,upload,cloudflare,digitalocean,dnsimple,dyn,gandi,googlecloud,linode,namecheap,ovh,rfc2136,route53,vultr +``` + +Caveats +------- + +### all platforms + +`go` (in which `caddy` is written) doesn't support privilege deescalation +(running on root to bind to port 80 and 443 and then switching to a non-root user). + +This isn't usually a problem, however, because the launchers (systemd et al) usually do. + +### darwin / macOS / OS X + +* `launchd` doesn't support privilege deescalation +* `authbind` doesn't work on recent versions of OS X + +However, you can use [ipfw](https://apple.stackexchange.com/questions/37418/how-can-i-open-port-80-so-a-non-root-process-can-bind-to-it) to locally port-forward. + +Also, `launchd` is a pain to configure. +There's not much in the way of official documuntation... +but there is some [great unofficial documentation](http://www.launchd.info/) +and [LaunchControl](http://www.soma-zone.com/LaunchControl/) +([direct download](http://www.soma-zone.com/download/files/LaunchControl_1.30.1.tar.bz2)) +makes it actually quite easy. +(ignore that the site looks like a 90s spam site - much like MakeMKV - it's actually legit) diff --git a/etc/systemd/system/caddy.service b/etc/systemd/system/caddy.service new file mode 100644 index 0000000..c24d436 --- /dev/null +++ b/etc/systemd/system/caddy.service @@ -0,0 +1,62 @@ +[Unit] +Description=Caddy HTTP/2 web server +Documentation=https://caddyserver.com/docs +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service + +[Service] +; Restart on crash (bad signal), but not on 'clean' failure (error exit code) +Restart=on-abnormal +; Allow up to 3 restarts within 10 seconds +; (it's unlikely that a user or properly-running script will do this) +StartLimitInterval=10 +StartLimitBurst=3 + +; User and group the process will run as +; (www-data is the de facto standard on most systems) +User=www-data +Group=www-data + +; Letsencrypt-issued certificates will be written to this directory. +Environment=CADDYPATH=/etc/ssl/caddy + +; Always set "-root" to something safe in case it gets forgotten in the Caddyfile. +WorkingDirectory=/etc/ssl/caddy +ExecStart=/usr/local/bin/caddy -log stdout -agree=true -conf=/etc/caddy/Caddyfile -root=/var/tmp +ExecReload=/bin/kill -USR1 $MAINPID + +; Limit the number of file descriptors; see `man systemd.exec` for more limit settings. +LimitNOFILE=1048576 +; Unmodified caddy is not expected to use more than that. +LimitNPROC=64 + +; Use private /tmp and /var/tmp, which are discarded after caddy stops. +PrivateTmp=true +; Use a minimal /dev +PrivateDevices=true +; Hide /home, /root, and /run/user. Nobody will steal your SSH-keys. +ProtectHome=true +; Make /usr, /boot, /etc and possibly some more folders read-only. +ProtectSystem=full +; … except /etc/ssl/caddy, because we want Letsencrypt-certificates there +; and /var/log/caddy, because we want a place where logs can go. +; This merely retains r/w access rights, it does not add any new. Must still be writable on the host! +ReadWritePaths=/etc/ssl/caddy /var/log/caddy + +; The following additional security directives only work with systemd v229 or later. +; They further retrict privileges that can be gained by caddy. Uncomment if you like. +; Note that you may have to add capabilities required by any plugins in use. +CapabilityBoundingSet=CAP_NET_BIND_SERVICE +AmbientCapabilities=CAP_NET_BIND_SERVICE +NoNewPrivileges=true + +; Caveat: Some plugins need additional capabilities. Add them to both lines above. +; - plugin "upload" needs: CAP_LEASE + +[Install] +WantedBy=multi-user.target + +; Learn more: +; +; * systemd: https://www.freedesktop.org/software/systemd/man/systemd.exec.html +; * why caddy's systemd file is what it is https://github.com/mholt/caddy/pull/1566/files diff --git a/etc/tmpfiles.d/caddy.conf b/etc/tmpfiles.d/caddy.conf new file mode 100644 index 0000000..42b1a87 --- /dev/null +++ b/etc/tmpfiles.d/caddy.conf @@ -0,0 +1,10 @@ +# /etc/tmpfiles.d/caddy.conf +# See https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html + +#Type Path Mode UID GID Age Argument +d /etc/caddy 0755 www-data www-data +d /etc/ssl/caddy 0750 www-data www-data +d /etc/ssl/caddy/acme 0750 www-data www-data +d /etc/ssl/caddy/oscp 0750 www-data www-data +d /var/log/caddy 0750 www-data www-data +#d /run/caddy 0755 www-data www-data diff --git a/install-caddy b/install-caddy new file mode 100755 index 0000000..6d924e2 --- /dev/null +++ b/install-caddy @@ -0,0 +1,423 @@ +#!/bin/bash + +# https://caddyserver.com/download +# https://caddyserver.com/download/build?os=darwin&arch=amd64&features=DNS,awslambda,cors,expires,filemanager,filter,git,hugo,ipfilter,jsonp,jwt,locale,mailout,minify,multipass,prometheus,ratelimit,realip,search,upload,cloudflare,digitalocean,dnsimple,dyn,gandi,googlecloud,linode,namecheap,ovh,rfc2136,route53,vultr + +installer_base="https://git.daplie.com/Daplie/daplie-snippets/raw/master" +caddy_systemd_service="$installer_base/etc/systemd/system/caddy.service" +caddy_systemd_tmpfiles="$installer_base/etc/tmpfiles.d/caddy.conf" +caddy_launchd_service="$installer_base/Library/LaunchDaemons/com.caddyserver.web.plist" + +all_oses="windows,darwin,linux,freebsd,openbsd" +all_arches="arm64,amd64,386,armv5,armv6l,armv7l" +all_plugins="DNS,awslambda,cors,expires,filemanager,filter,git,hugo,ipfilter,jsonp,jwt,locale,mailout,minify,multipass,prometheus,ratelimit,realip,search,upload,cloudflare,digitalocean,dnsimple,dyn,gandi,googlecloud,linode,namecheap,ovh,rfc2136,route53,vultr" + +caddy_os="" +default_os="" +caddy_arch="" +default_arch="" +caddy_arm="" +caddy_features="" +caddy_user=www-data +caddy_group=www-data +install_path="/usr/local/bin" +http_get="" +http_opts="" +http_out="" + +caddy_bin="caddy" +caddy_dl_ext=".tar.gz" + +detect_http_get() +{ + if type -p curl >/dev/null 2>&1; then + http_get="curl" + http_opts="-fsSL" + http_out="-o" + #curl -fsSL "$caddy_url" -o "$PREFIX/tmp/$caddy_pkg" + elif type -p wget >/dev/null 2>&1; then + http_get="wget" + http_opts="--quiet" + http_out="-O" + #wget --quiet "$caddy_url" -O "$PREFIX/tmp/$caddy_pkg" + else + echo "Aborted, could not find curl or wget" + return 7 + fi +} + +detect_arch() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + ######################### + # Which OS and version? # + ######################### + + default_arch="unknown" + + # NOTE: `uname -m` is more accurate and universal than `arch` + # See https://en.wikipedia.org/wiki/Uname + unamem="$(uname -m)" + if [[ $unamem == *aarch64* ]]; then + default_arch="arm64" + elif [[ $unamem == *64* ]]; then + default_arch="amd64" + elif [[ $unamem == *86* ]]; then + default_arch="386" + elif [[ $unamem == *armv5* ]]; then + default_arch="armv5" + elif [[ $unamem == *armv6l* ]]; then + default_arch="armv6l" + elif [[ $unamem == *armv7l* ]]; then + default_arch="armv7l" + else + default_arch="$unamem" + fi + + trap ERR +} + +detect_os() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + default_os="unsupported" + + uname="$(uname)" + #declare -u unameu=$uname # requires bash v4+, not supported on macOS + unameu="$(echo $uname | tr '/a-z/' '/A-Z/')" + if [[ ${unameu} == *DARWIN* ]]; then + default_os="darwin" + vers=$(sw_vers) + version=${vers##*ProductVersion:} + IFS='.' read OSX_MAJOR OSX_MINOR _ <<<"$version" + + # Major + if ((OSX_MAJOR < 10)); then + echo "Aborted, unsupported OS X version (9-)" + return 3 + fi + if ((OSX_MAJOR > 10)); then + echo "Aborted, unsupported OS X version (11+)" + return 4 + fi + + # Minor + if ((OSX_MINOR < 5)); then + echo "Aborted, unsupported OS X version (10.5-)" + return 5 + fi + elif [[ ${unameu} == *LINUX* ]]; then + default_os="linux" + elif [[ ${unameu} == *FREEBSD* ]]; then + default_os="freebsd" + elif [[ ${unameu} == *OPENBSD* ]]; then + default_os="openbsd" + elif [[ ${unameu} == *WIN* ]]; then + # Should catch cygwin + default_os="windows" + caddy_dl_ext=".zip" + caddy_bin=$caddy_bin.exe + else + default_os="$uname" + fi + + trap ERR +} + +detect_target() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + detect_arch + detect_os + + while [[ $# -gt 0 ]]; do + key="$1" + case $key in + --arch) + caddy_arch="$2" + shift # past argument + ;; + --os) + caddy_os="$2" + shift # past argument + ;; + -h|--help) + echo "" + echo "Usage: install-caddy [plugin1,plugin2,...]" + echo "" + echo "Optional arguments (defaults shown)" + echo " --os ${default_os} # options: $(echo $all_oses | tr "," " ")" + echo " --arch ${default_arch}${caddy_arm} # options: $(echo $all_arches | tr "," " ")" + echo "" + echo "Features:" + echo "$(echo $all_plugins)" + echo "" + exit 0 + ;; + -*) + # unknown argument + echo "unrecognized argument '$1'" + exit 13 + ;; + *) + # unknown value + if [ -z "${caddy_features}" ]; then + caddy_features=$1 + else + echo "unrecognized value '$1'" + exit 1 + fi + ;; + esac + shift # past argument or value + done + + # use commandline-specified caddy os or the host OS + if [ -z "$caddy_os" ]; then + caddy_os=$default_os + fi + + if [[ ${caddy_os} == "darwin" ]]; then + caddy_user=_www + caddy_group=_www + caddy_dl_ext=".zip" + elif [[ ${caddy_os} == "linux" ]]; then + ignore=true + elif [[ ${caddy_os} == "freebsd" ]]; then + ignore=true + elif [[ ${caddy_os} == "openbsd" ]]; then + ignore=true + elif [[ ${caddy_os} == "windows" ]]; then + caddy_dl_ext=".zip" + caddy_bin=$caddy_bin.exe + else + echo "Aborted, unsupported or unknown os: $caddy_os" + return 6 + fi + + # use commandline-specified caddy arch or the host arch + if [ -z "$caddy_arch" ]; then + caddy_arch=$default_arch + fi + + if [[ ${caddy_arch} == "arm64" ]]; then + ignore=true + elif [[ ${caddy_arch} == "amd64" ]]; then + ignore=true + elif [[ ${caddy_arch} == "386" ]]; then + ignore=true + elif [[ ${caddy_arch} == "armv5" ]]; then + caddy_arch="arm" + caddy_arm=5 + elif [[ ${caddy_arch} == "armv6l" ]]; then + caddy_arch="arm" + caddy_arm=6 + elif [[ ${caddy_arch} == "armv7l" ]]; then + caddy_arch="arm" + caddy_arm=7 + else + echo "Aborted, unsupported or unknown architecture: $caddy_arch" + return 2 + fi + + trap ERR +} + +download_caddy() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + ######################## + # Download and extract # + ######################## + + echo "Downloading Caddy for $caddy_os/$caddy_arch$caddy_arm..." + #caddy_file="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom$caddy_dl_ext" + caddy_pkg="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom$caddy_dl_ext" + caddy_dir="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom" + # new.caddyserver.com + #caddy_url="https://caddyserver.com/download/$caddy_os/$caddy_arch$caddy_arm?plugins=$caddy_features" + # current caddyserver.com + caddy_url="https://caddyserver.com/download/build?os=$caddy_os&arch=$caddy_arch&arm=$caddy_arm&features=$caddy_features" + echo "$caddy_url" + + # Use $PREFIX for compatibility with Termux on Android + rm -rf "$PREFIX/tmp/$caddy_pkg" "$PREFIX/tmp/$caddy_dir" + + $http_get $http_opts "$caddy_url" $http_out "$PREFIX/tmp/$caddy_pkg" + + echo "Extracting..." + case "$caddy_pkg" in + *.zip) unzip -o "$PREFIX/tmp/$caddy_pkg" "$caddy_bin" -d "$PREFIX/tmp/$caddy_dir/" ;; + #*.tar.gz) tar -xzf "$PREFIX/tmp/$caddy_file" -C "$PREFIX/tmp/" "$caddy_bin" ;; + *.tar.gz) mkdir -p "$PREFIX/tmp/$caddy_dir/"; tar -xzf "$PREFIX/tmp/$caddy_pkg" -C "$PREFIX/tmp/$caddy_dir/" ;; + esac + + trap ERR +} + +install_caddy() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + if [ "$caddy_os" != "$default_os" ]; then + echo "caddy os does not match $default_os, skipping install" + fi + if [ "$caddy_arch" != "$default_arch" ]; then + echo "caddy arch does not match $default_arch, skipping install" + fi + + # Termux on Android has $PREFIX set which already ends with /usr + if [[ -n "$ANDROID_ROOT" && -n "$PREFIX" ]]; then + install_path="$PREFIX/bin" + fi + + # Fall back to /usr/bin if necessary + if [[ ! -d $install_path ]]; then + install_path="/usr/bin" + fi + + # Not every platform has or needs sudo (see issue #40) + ((EUID)) && [[ -z "$ANDROID_ROOT" ]] && sudo_cmd="sudo" + + # Back up existing caddy, if any + caddy_cur_ver="$("$caddy_bin" --version 2>/dev/null | cut -d ' ' -f2)" + if [[ $caddy_cur_ver ]]; then + # caddy of some version is already installed + caddy_path="$(type -p "$caddy_bin")" + caddy_backup="${caddy_path}_$caddy_cur_ver" + echo "Backing up $caddy_path to $caddy_backup" + echo "(Password may be required.)" + $sudo_cmd mv "$caddy_path" "$caddy_backup" + fi + + echo "Putting caddy in $install_path (may require password)" + $sudo_cmd mv "$PREFIX/tmp/$caddy_dir/$caddy_bin" "$install_path/$caddy_bin" + + $sudo_cmd mkdir -p $PREFIX/etc/caddy + if [ ! -f "$PREFIX/etc/caddy/Caddyfile" ] || [ -z "$(cat $PREFIX/etc/caddy/Caddyfile)" ]; then + cat <<'CADDY_EOF' >> $PREFIX/tmp/$caddy_dir/Caddyfile +http://localhost { + root /srv/www/localhost +} +CADDY_EOF + $sudo_cmd mv "$PREFIX/tmp/$caddy_dir/Caddyfile" "$PREFIX/etc/caddy/Caddyfile" + fi + + $sudo_cmd mkdir -p $PREFIX/etc/ssl/caddy + $sudo_cmd mkdir -p $PREFIX/var/log/caddy + $sudo_cmd mkdir -p $PREFIX/srv/www/localhost + + $sudo_cmd chmod +x "$install_path/$caddy_bin" + $sudo_cmd chmod 755 $PREFIX/etc/caddy + $sudo_cmd chmod 755 $PREFIX/etc/caddy + $sudo_cmd chmod 750 $PREFIX/etc/ssl/caddy + $sudo_cmd chmod 750 $PREFIX/var/log/caddy + $sudo_cmd chmod 755 $PREFIX/srv/www + $sudo_cmd chmod 755 $PREFIX/srv/www/localhost + + if [ ! -f "$PREFIX/srv/www/localhost/index.html" ] || [ -z "$(cat $PREFIX/srv/www/localhost)" ]; then + cat <<'CADDY_EOF' >> $PREFIX/tmp/$caddy_dir/index.html + + + + Welcome to Caddy! + + +

Welcome to Caddy!

+ + +CADDY_EOF + $sudo_cmd mv "$PREFIX/tmp/$caddy_dir/index.html" "$PREFIX/srv/www/localhost/index.html" + fi + + if setcap_cmd=$(type -p setcap); then + $sudo_cmd $setcap_cmd cap_net_bind_service=+ep "$install_path/$caddy_bin" + fi + $sudo_cmd rm -- "$PREFIX/tmp/$caddy_pkg" + + echo "" + # check installation + $caddy_bin --version + + echo "Successfully installed" + + echo "" + echo "Trying to set permissions to $caddy_user:$caddy_group" + echo '(if this fails please file a bug with the output of "uname -a" and "cat /etc/group")' + + # TODO if this fails perhaps catch and create a www-data group? + # TODO grep $caddy_user $PREFIX/etc/passwd + # TODO grep $caddy_group $PREFIX/etc/group + # TODO adduser $caddy_user + $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/etc/caddy + $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/etc/ssl/caddy + $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/var/log/caddy + $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/srv/www + + echo "" + echo "Successfully changed permissions" + echo "" + + if [ -d "$PREFIX/etc/systemd/system" ]; then + echo "" + echo "Installing as systemd service" + echo "" + $http_get $http_opts "$caddy_systemd_service" $http_out $PREFIX/tmp/$caddy_dir/caddy.service + $sudo_cmd mv $PREFIX/tmp/$caddy_dir/caddy.service $PREFIX/etc/systemd/system/caddy.service + $sudo_cmd chown -R root:root $PREFIX/etc/systemd/system/caddy.service + $sudo_cmd chmod 644 $PREFIX/etc/systemd/system/caddy.service + + $http_get $http_opts "$caddy_systemd_tmpfiles" $http_out $PREFIX/tmp/$caddy_dir/caddy.conf + $sudo_cmd mv $PREFIX/tmp/$caddy_dir/caddy.conf $PREFIX/etc/tmpfiles.d/caddy.conf + $sudo_cmd chown -R root:root $PREFIX/etc/tmpfiles.d/caddy.conf + $sudo_cmd chmod 644 $PREFIX/etc/tmpfiles.d/caddy.conf + + $sudo_cmd systemctl daemon-reload + $sudo_cmd systemctl start caddy.service + elif [ -d "/Library/LaunchAgents" ]; then + echo "" + echo "Installing as launchd service" + echo "" + # See http://www.launchd.info/ + $http_get $http_opts "$caddy_launchd_service" $http_out $PREFIX/tmp/$caddy_dir/com.caddyserver.web.plist + $sudo_cmd mv $PREFIX/tmp/$caddy_dir/com.caddyserver.web.plist /Library/LaunchAgents/com.caddyserver.web.plist + $sudo_cmd chown root:wheel /Library/LaunchAgents/com.caddyserver.web.plist + $sudo_cmd chmod 0644 /Library/LaunchAgents/com.caddyserver.web.plist + $sudo_cmd launchctl load -w /Library/LaunchAgents/com.caddyserver.web.plist + else + echo "" + echo "Unknown system service init type. You must install as a system service manually." + echo '(please file a bug with the output of "uname -a")' + echo "" + fi + + # cleanup + $sudo_cmd rm -rf -- "$PREFIX/tmp/$caddy_dir/" + + trap ERR +} + +run() +{ + trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR + + detect_http_get + detect_target "$@" + download_caddy "$@" + + if [ -f "$PREFIX/tmp/$caddy_dir/install.sh" ]; then + chmod a+x $PREFIX/tmp/$caddy_dir/install.sh + $PREFIX/tmp/$caddy_dir/install.sh "$PREFIX/tmp/$caddy_dir" "$@" + else + install_caddy "$@" + fi + + trap ERR + return 0 +} + +run "$@"