├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README-DOCKER.md ├── README.md ├── build.sh ├── configs └── sysctl.conf ├── go.sh ├── test.sh ├── to_copy ├── __init__.py ├── etc │ ├── cron.d │ │ └── cloak │ ├── dh2048.pem │ ├── encryptme-stats.conf │ ├── iptables.eme.rules.j2 │ ├── openvpn │ │ └── openvpn.conf.j2 │ └── strongswan │ │ ├── ipsec.conf.j2 │ │ ├── ipsec.d │ │ └── cacerts │ │ │ └── dst-root.crt │ │ ├── ipsec.secrets.j2 │ │ └── strongswan.conf.j2 ├── opt │ └── dns-filter │ │ ├── __init__.py │ │ ├── daemon.py │ │ └── server.py ├── run.sh └── usr │ ├── __init__.py │ ├── bin │ ├── __init__.py │ ├── boot-cert.sh │ ├── boot-expired-revoked-certs.sh │ ├── obtain-cert-end-date.sh │ ├── openvpn-client-disconnect.sh │ ├── openvpn-on-connect.sh │ ├── pep-filter.sh │ ├── refresh-crls.sh │ ├── refresh-wireguard.py │ ├── reload-certificate.sh │ ├── renew-cert.sh │ ├── template.py │ └── update-pki.sh │ └── local │ └── unbound-1.7 │ ├── etc │ └── unbound │ │ └── unbound.conf │ └── sbin │ └── filter_client.py └── to_extract └── unbound-1.7.tar.gz /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.*.sw? 2 | test-*.sh 3 | encryptme_conf 4 | .git* 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .*.sw? 3 | encryptme_conf/ 4 | test-*.sh 5 | sftp-config.json 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:7 2 | 3 | # Core system dependencies 4 | RUN yum clean all && \ 5 | yum -y -q update && \ 6 | yum -y -q install epel-release && \ 7 | yum -y update && \ 8 | yum -y -q install \ 9 | cronie \ 10 | python36 \ 11 | python36-devel \ 12 | python36-pip \ 13 | git \ 14 | jq \ 15 | vim \ 16 | gcc \ 17 | bind-utils \ 18 | && \ 19 | yum -y -q install \ 20 | openvpn \ 21 | strongswan \ 22 | kmod \ 23 | letsencrypt \ 24 | curl \ 25 | socat \ 26 | ipset \ 27 | https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ 28 | && \ 29 | curl -o /etc/yum.repos.d/jdoss-wireguard-epel-7.repo \ 30 | https://copr.fedorainfracloud.org/coprs/jdoss/wireguard/repo/epel-7/jdoss-wireguard-epel-7.repo && \ 31 | yum -y -q install wireguard-tools && \ 32 | yum clean all && \ 33 | rm -rf /var/cache/yum 34 | 35 | # System configuration 36 | RUN useradd -s /sbin/nologin unbound 37 | 38 | # Latest python packaging tools 39 | RUN python3.6 -m pip install --upgrade --no-cache-dir pip && \ 40 | python3.6 -m pip install --upgrade --no-cache-dir setuptools 41 | 42 | # Container versioning for release tracking 43 | LABEL version=0.13.2 44 | RUN echo "v0.13.2" > /container-version-id 45 | 46 | # Project specific dependencies 47 | ARG build_time=${build_time:-x} 48 | ARG repo_branch=${repo_branch:-master} 49 | RUN python3.6 -m pip install --no-cache-dir \ 50 | "git+https://github.com/encryptme/private-end-points.git@$repo_branch" \ 51 | jinja2 \ 52 | python-pidfile \ 53 | && \ 54 | ln -s /usr/sbin/strongswan /usr/sbin/ipsec 55 | 56 | # Python stats daemon for health monitoring 57 | ARG repo_branch=${repo_branch:-master} 58 | ADD https://github.com/encryptme/private-end-points-docker-stats/archive/$repo_branch.zip \ 59 | /tmp/encryptme-metrics.zip 60 | RUN python3.6 -m pip install --no-cache-dir /tmp/encryptme-metrics.zip && \ 61 | rm /tmp/encryptme-metrics.zip 62 | 63 | # Generic files to extract/copy into the repo 64 | ADD to_extract /tmp/to_extract 65 | RUN tar zxf /tmp/to_extract/unbound-1.7.tar.gz -C /usr/local/ 66 | RUN rm -rf /tmp/to_extract 67 | ADD to_copy/ / 68 | 69 | 70 | ENTRYPOINT ["/run.sh"] 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Cloak Holdings, LLC 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README-DOCKER.md: -------------------------------------------------------------------------------- 1 | # BUILDING AN IMAGE 2 | 3 | Building the docker image is automated via the use of `build.sh`. It makes it 4 | easier to supply the Dockerfile variables and tag/push the image. 5 | 6 | You must use `docker login` prior to pushing images to Docker Hub. 7 | 8 | 9 | # build.sh USAGE 10 | 11 | ``` 12 | Usage: build.sh [OPTIONS] [DOCKER ARGS] 13 | 14 | OPTIONS: 15 | 16 | -b|--branch REPO Branch for client and stats repos (default: based on env) 17 | -e|--env ENV Env to build/push (dev, staging, prod) (default: prod) 18 | -h|--help This information 19 | -t|--tag TAG Override image tag 20 | -p|--push Automatically push to Docker hub 21 | -t|--tag Specify explicit tag (overrides --env) 22 | 23 | EXAMPLE: 24 | 25 | $ build.sh --env dev --branch master --tag jsmith/encryptme-pep --push 26 | ``` 27 | 28 | 29 | ## DOCKER VARIABLES 30 | 31 | If building natively with Docker there are a few options: 32 | 33 | - repo_branch: which branch to use for the client and stats repos (default=master) 34 | - build_time: an optional build-time (e.g. helps cache-bust file changes) 35 | 36 | 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPLOYING 2 | 3 | This container expects a CentOS 7 or compatible host for IPSEC. 4 | Server registration keys can be created by Encrypt.me Teams on the network 5 | management section of the the team control panel. 6 | 7 | 8 | # OVERVIEW 9 | 10 | The script "go.sh" (see `./go.sh --help`) automates the Docker container setup 11 | and end-point registration. It is designed to automate the entire setup process 12 | via: 13 | 14 | `./go.sh init --pull-image && ./go.sh run` 15 | 16 | It is designed to run locally or via SSH to another host (e.g. VM in the cloud, 17 | via the --remote option) either attended or unattended. Docker must be 18 | installed and running on the target host. 19 | 20 | ``` 21 | export SSL_EMAIL=user@example.com 22 | ENCRYPTME_SLOT_KEY=one-time-key ./go.sh init --non-interactive && ./go.sh run 23 | ``` 24 | 25 | 26 | # MOUNTPOINTS / PERSISTENT DATA 27 | 28 | When running `./go.sh init` a directory named `encryptme_conf/` will be created 29 | in your current directory. This stores configuration files, an API key (e.g. 30 | for fetching fresh PKI certificates), certs, and keys. It needs to persist 31 | between container restarts. 32 | 33 | 34 | # SSL CERTIFICATES 35 | 36 | A 90-day SSL certicate is automatically obtained from LetsEncrypt. Domain 37 | ownership is validated via the HTTP ACME challenge. This is automatically 38 | renewed for you. 39 | 40 | 41 | # go.sh FULL USAGE 42 | 43 | ### ENVIRONMENT VARIABLES 44 | 45 | | Variable | Notes | 46 | |----------|-------| 47 | | SSL_EMAIL | Registration username - required if letsencrypt enabled (default). | 48 | | ENCRYPTME_SLOT_KEY | Required when attempting to register your server. | 49 | | LETSENCRYPT_DISABLED| 1 = Disable automatic letsencrypt | 50 | 51 | 52 | ``` 53 | usage: ./go.sh [--remote|-r HOST] [ACTION ARGS] ACTION 54 | 55 | Initialize an Encrypt.me private-end point server from a Docker image. Run 56 | './go.sh init' and then './go.sh run' to set everything up. Any missing 57 | parameters (registration key and name for init; email only for run) will be 58 | prompted for if missing. 59 | 60 | If running with --remote it must be used as the first argument. 61 | 62 | 63 | ACTIONS: 64 | 65 | init initialize a docker container and register this server 66 | run set the private-end point to run 67 | clean remove the private end-point container, images, and configs 68 | reset stop/remove any current instance and remove configs 69 | shell Run the image with a custom entrypoint to start bash 70 | 71 | 72 | GENERIC OPTIONS: 73 | -c|--conf-dir DIR Directory to use/create for private configs/certs 74 | -d|--dryrun|--dry-run Run without making changes 75 | (default: /path/to/encryptme_conf) 76 | -e|--email Email email address for LetsEncrypt certs 77 | -h|--help Show this message 78 | -i|--image IMAGE Docker image to use (default: encryptme/pep) 79 | -n|--name NAME Container name (default: encryptme) 80 | -D|--dns-check Attempt to do AWS/DO DNS validation 81 | -t|--cert-type TYPE Certificate type to use e.g. 'letsencypt', 'comodo' 82 | (default: letsencrypt) 83 | -v|--verbose Verbose debugging info 84 | -l|--logging Enable some logging, eg IPSEC via /dev/log 85 | 86 | INIT OPTIONS: 87 | --api-url URL Use custom URL for Encrypt.me server API 88 | --non-interactive Do not attempt to allocate TTYs (e.g. to prompt for 89 | missing params) 90 | --server-name NAME Fully-qualified domain name for this VPN end-point 91 | --slot-key ID Slot registration key from the Encrypt.me website. 92 | 93 | RUN OPTIONS: 94 | -R|--restart Restarts running services if already running 95 | 96 | PRIVACY/SECURITY OPTIONS: 97 | -P|--pull-image Pull Docker Hub image? (default: off) 98 | -U|--update Run WatchTower to keep VPN container up-to-date 99 | (default: off) 100 | -S|--stats Send generic bandwidth/health stats (default: off) 101 | --stats-server Specify an alternate http(s) server to receive stats 102 | --stats-extra Include extra details in stats, such as server_id, target_id, 103 | server name (fqdn) and target name (default: off) 104 | 105 | 106 | EXAMPLES: 107 | 108 | # launch an auto-updating image with health reporting using the official 109 | # image and ensure our AWS/DO public IP matches our FQDN 110 | ./go.sh init -S -U -P -D 111 | 112 | # run the newly initialized server 113 | ./go.sh run 114 | ``` 115 | 116 | 117 | # WIREGUARD PROTOCOL 118 | 119 | Optionally support for the WIREGUARD protocol can be enabled by installing the 120 | wireguard kernel module on the target host. 121 | 122 | ``` 123 | # Specific for CentOS 7 124 | 125 | sudo yum install epel-release https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm 126 | sudo yum install yum-plugin-elrepo 127 | sudo yum install kmod-wireguard 128 | ``` 129 | 130 | Instructions to install WIREGUARD on multiple operating systems can be found in the [official documentation](https://www.wireguard.com/install/). 131 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -u 2 | 3 | BASE_DIR=$(cd $(dirname "$0") && pwd -P) 4 | SCRIPT_NAME=$(basename "$0") 5 | 6 | fail() { 7 | echo "$@" >&2 8 | exit 1 9 | } 10 | 11 | usage() { 12 | cat <&1 || fail "Failed to locate 'docker' binary" 73 | cd "$BASE_DIR" || fail "Failed to CD to our base dir?" 74 | 75 | 76 | tag="encryptme/pep" 77 | if [ "$env" = 'dev' ]; then 78 | tag="$tag-dev" 79 | [ -n "$branch" ] || fail "Must specify branch to use for 'dev'" 80 | elif [ "$env" = 'staging' ]; then 81 | tag="$tag-staging" 82 | [ -n "$branch" ] || branch='staging' 83 | else 84 | [ -n "$branch" ] || branch='master' 85 | fi 86 | [ -n "$user_tag" ] && tag="$user_tag" 87 | 88 | echo "Building '$tag' for '$env' with PEP client repo branch '$branch'" 89 | echo 90 | echo ' ----======----' 91 | docker build . -t "$tag" \ 92 | --build-arg build_time=$(date +%s) \ 93 | --build-arg repo_branch="$branch" \ 94 | "${docker_args[@]}" \ 95 | || fail "Failed to build '$tag' with repo branch '$branch'" 96 | echo ' ----======----' 97 | echo 98 | echo 99 | 100 | if [ $push -eq 1 ]; then 101 | docker push "$tag" || fail "Failed to push '$tag'" 102 | else 103 | echo "Skipped 'docker push \"$tag\"'" 104 | fi 105 | 106 | 107 | exit 0 108 | -------------------------------------------------------------------------------- /configs/sysctl.conf: -------------------------------------------------------------------------------- 1 | net.core.somaxconn=1024 2 | net.core.netdev_max_backlog=25000 3 | net.core.rmem_default=262144 4 | net.core.rmem_max=16777216 5 | net.core.wmem_default=262144 6 | net.core.wmem_max=16777216 7 | net.ipv4.tcp_rmem=262144 262144 16777216 8 | net.ipv4.tcp_wmem=262144 262144 16777216 9 | net.ipv4.tcp_max_syn_backlog=1000 10 | net.ipv4.tcp_slow_start_after_idle=0 11 | net.core.optmem_max=16777216 12 | net.netfilter.nf_conntrack_max=1008768 13 | -------------------------------------------------------------------------------- /go.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Initialize and run an Encrypt.me private end-point via Docker 4 | 5 | # TODO: implement pre-supplied SSL certs (e.g. from Comodo or some other provider) 6 | 7 | 8 | BASE_DIR=$(cd $(dirname "$0") && pwd -P) 9 | SCRIPT_NAME=$(basename "$0") 10 | SCRIPT_PATH="$0" 11 | 12 | # dynamic params 13 | [ $UID -eq 0 ] && conf_dir=/etc/encryptme || conf_dir="$BASE_DIR/encryptme_conf" 14 | ssl_email= 15 | no_email=0 16 | server_name= 17 | slot_key= 18 | action= 19 | pull_image=0 20 | send_stats=0 21 | stats_server="" 22 | stats_args="" 23 | api_url= 24 | dns_check=0 25 | dryrun=0 26 | non_interactive=0 27 | verbose=0 28 | restart=0 29 | tune_network=0 30 | cert_type="letsencrypt" 31 | eme_img="encryptme/pep" 32 | name="encryptme" 33 | logging=0 34 | 35 | 36 | usage() { 37 | cat << EOF 38 | usage: $0 [--remote|-r HOST] [ACTION ARGS] ACTION 39 | 40 | Initialize an Encrypt.me private-end point server from a Docker image. Run 41 | './go.sh init' and then './go.sh run' to set everything up. Any missing 42 | parameters (registration key and name for init; email only for run) will be 43 | prompted for if missing. 44 | 45 | If running with --remote it must be used as the first argument. 46 | 47 | 48 | ACTIONS: 49 | 50 | init initialize a docker container and register this server 51 | run set the private-end point to run 52 | clean remove the private end-point container, images, and configs 53 | reset stop/remove any current instance and remove configs 54 | shell Run the image with a custom entrypoint to start bash 55 | 56 | 57 | GENERIC OPTIONS: 58 | -c|--conf-dir DIR Directory to use/create for private configs/certs 59 | (default: $conf_dir) 60 | -d|--dryrun|--dry-run Run without making changes 61 | -e|--email Email email address for LetsEncrypt certs 62 | -h|--help Show this message 63 | -i|--image IMAGE Docker image to use (default: $eme_img) 64 | -n|--name NAME Container name (default: $name) 65 | -D|--dns-check Attempt to do AWS/DO DNS validation 66 | -t|--cert-type TYPE Certificate type to use e.g. 'letsencypt', 'comodo' 67 | (default: $cert_type) 68 | -v|--verbose Verbose debugging info 69 | -l|--logging Enable some logging, eg IPSEC via /dev/log 70 | -T|--tune-network Add 'sysctl.conf' tuning to 'encryptme.conf' 71 | --no-email Ignore -e option and use --register-unsafely-without-email 72 | instead 73 | 74 | INIT OPTIONS: 75 | --api-url URL Use custom URL for Encrypt.me server API 76 | --non-interactive Do not attempt to allocate TTYs (e.g. to prompt for 77 | missing params) 78 | --server-name NAME Fully-qualified domain name for this VPN end-point 79 | --slot-key ID Slot/server registration key from the Encrypt.me website. 80 | 81 | RUN OPTIONS: 82 | -R|--restart Restarts running services if already running 83 | 84 | PRIVACY/SECURITY OPTIONS: 85 | -P|--pull-image Pull Docker Hub image? (default: off) 86 | -S|--stats Send generic bandwidth/health stats (default: off) 87 | --stats-server SERVER Specify an alternate http(s) server to receive stats 88 | --stats-key KEY Optional authorization key for sending stats. 89 | --stats-extra Include extra details in stats, such as server_id, 90 | target_id, server name (fqdn) and target name 91 | (default: off) 92 | 93 | 94 | EXAMPLES: 95 | 96 | # launch an auto-updating image with health reporting using the official 97 | # image and ensure our AWS/DO public IP matches our FQDN 98 | ./go.sh init -S -U -P -D 99 | 100 | # run the newly initialized server 101 | ./go.sh run 102 | 103 | EOF 104 | } 105 | 106 | fail() { 107 | echo "! $1" >&2 108 | exit 1 109 | } 110 | 111 | rem() { 112 | [ "$verbose" -eq 1 ] && echo "+ [$@]" >&2 113 | } 114 | 115 | cmd() { 116 | local retval=0 117 | if [ $dryrun -eq 1 ]; then 118 | echo "# $@" 119 | else 120 | [ $verbose -eq 1 ] && echo "$ $@" >&2 121 | "$@" 122 | retval=$? 123 | fi 124 | return $retval 125 | } 126 | 127 | collect_args() { 128 | while [ $no_email -eq 0 -a -z "$ssl_email" ]; do 129 | read -p "Enter your the email address for your LetsEncrypt certificate: " ssl_email 130 | done 131 | [ "$action" = 'init' ] && { 132 | cat << EOF 133 | --------------------------------------------------------------------- 134 | You can obtain server registration keys from the Encrypt.me customer 135 | portal. Once you have created a network, self-hosted target, and 136 | server you will be given a server registration key to use with this 137 | script. 138 | --------------------------------------------------------------------- 139 | 140 | EOF 141 | while [ -z "$slot_key" ]; do 142 | read -p $'\n'"Enter your Encrypt.me server registration key: " slot_key 143 | done 144 | } 145 | } 146 | 147 | run_remote() { 148 | local remote_host="$1" 149 | shift 150 | scp -q "$SCRIPT_PATH" "$remote_host":go.sh \ 151 | || fail "Couldn't copy script to $remote_host" 152 | ssh -qt "$remote_host" ./go.sh "$@" \ 153 | || fail "Remote on $remote_host execution failed" 154 | ssh -q "$remote_host" rm go.sh \ 155 | || fail "Failed to remove go.sh from $remote_host" 156 | } 157 | 158 | docker_cleanup() { 159 | local do_restart=$restart 160 | if [ "$1" = "-f" ]; then 161 | shift 162 | do_restart=1 163 | fi 164 | local container="$1" 165 | local running=$(cmd docker inspect --format='{{.State.Running}}' "$container" 2>/dev/null) 166 | rem "Container '$container' has running=$running" 167 | [ $do_restart -eq 1 -a "$running" = "true" ] && 168 | (cmd docker kill "$container" || 169 | fail "Failed to kill running container $container") 170 | [ $do_restart -eq 1 -a ! -z "$running" ] && 171 | (cmd docker rm "$container" || 172 | fail "Failed to remove container $container") 173 | } 174 | 175 | server_init() { 176 | local init_args=(run --rm) 177 | [ $non_interactive -eq 0 ] && init_args=("${init_args[@]}" -it) 178 | [ "$logging" = 1 ] \ 179 | && logging_args="-e ENCRYPTME_LOGGING=1 -v /dev/log:/dev/log" \ 180 | || logging_args='' 181 | init_args=( 182 | "${init_args[@]}" 183 | --name "$name" 184 | -e SSL_EMAIL="$ssl_email" \ 185 | -e NO_EMAIL=$no_email \ 186 | -e ENCRYPTME_SLOT_KEY="$slot_key" \ 187 | -e ENCRYPTME_API_URL="$api_url" \ 188 | -e ENCRYPTME_SERVER_NAME="$server_name" \ 189 | -e ENCRYPTME_STATS=$send_stats \ 190 | -e ENCRYPTME_STATS_SERVER=$stats_server \ 191 | -e ENCRYPTME_STATS_ARGS="$stats_args" \ 192 | -e ENCRYPTME_VERBOSE=$verbose \ 193 | -e ENCRYPTME_TUNE_NETWORK=$tune_network \ 194 | -e INIT_ONLY=1 \ 195 | -e DNS_TEST_IP="$dns_test_ip" \ 196 | -v "$conf_dir:/etc/encryptme" \ 197 | -v "$conf_dir/letsencrypt:/etc/letsencrypt" \ 198 | -v /lib/modules:/lib/modules:ro \ 199 | --privileged \ 200 | --net host \ 201 | $logging_args \ 202 | "$eme_img" 203 | ) 204 | docker_cleanup "$name" 205 | cmd docker "${init_args[@]}" || fail "Failed to register end-point" 206 | 207 | # TODO: make more dynamic based on OS (e.g. at least check for systemctl before using it) 208 | if [ -f /etc/apparmor.d/usr.lib.ipsec.charon -o -f /etc/apparmor.d/usr.lib.ipsec.stroke ]; then 209 | rem Removing /etc/apparmor.d/usr.lib.ipsec.charon 210 | # TODO we should install a better charon/stroke apparmor config 211 | cmd rm -f /etc/apparmor.d/usr.lib.ipsec.charon 212 | cmd rm -f /etc/apparmor.d/usr.lib.ipsec.stroke 213 | cmd systemctl reload apparmor 214 | cmd aa-remove-unknown 215 | fi 216 | return 0 217 | } 218 | 219 | server_shell() { 220 | cmd docker run \ 221 | -it \ 222 | --entrypoint bash \ 223 | -e ENCRYPTME_API_URL="$api_url" \ 224 | -v "$conf_dir:/etc/encryptme" \ 225 | -v "$conf_dir/letsencrypt:/etc/letsencrypt" \ 226 | -v /proc:/hostfs/proc:ro \ 227 | -v /var/run/docker.sock:/var/run/docker.sock \ 228 | -v /lib/modules:/lib/modules:ro \ 229 | --privileged \ 230 | --net host \ 231 | "$eme_img" 232 | } 233 | 234 | server_run() { 235 | docker_cleanup "$name" 236 | logging_args="" 237 | [ "$logging" = 1 ] && logging_args="-e ENCRYPTME_LOGGING=1 -v /dev/log:/dev/log" 238 | cmd docker run -d --name "$name" \ 239 | -e SSL_EMAIL="$ssl_email" \ 240 | -e NO_EMAIL=$no_email \ 241 | -e ENCRYPTME_API_URL="$api_url" \ 242 | -e ENCRYPTME_VERBOSE=$verbose \ 243 | -e ENCRYPTME_STATS=$send_stats \ 244 | -e ENCRYPTME_STATS_SERVER=$stats_server \ 245 | -e ENCRYPTME_STATS_ARGS="$stats_args" \ 246 | -e ENCRYPTME_TUNE_NETWORK=$tune_network \ 247 | -v "$conf_dir:/etc/encryptme" \ 248 | -v "$conf_dir/letsencrypt:/etc/letsencrypt" \ 249 | -v /lib/modules:/lib/modules:ro \ 250 | -v /proc:/hostfs/proc:ro \ 251 | -v /var/run/docker.sock:/var/run/docker.sock \ 252 | --privileged \ 253 | --net host \ 254 | --restart always \ 255 | $logging_args \ 256 | "$eme_img" 257 | } 258 | 259 | server_reset() { 260 | docker_cleanup -f "$name" 261 | cmd rm -rf "$conf_dir" 262 | } 263 | 264 | server_cleanup() { 265 | server_reset 266 | cmd docker rmi "$eme_img" 267 | } 268 | 269 | 270 | #echo " -- Running $SCRIPT_NAME on $HOSTNAME (PID: $$); dryrun=$dryrun" >&2 271 | #echo " -- ARGS: $@" >&2 272 | 273 | # gather up args 274 | arg_count=0 275 | while [ $# -gt 0 ]; do 276 | arg="$1" 277 | shift 278 | case "$arg" in 279 | --dryrun|dry-run|-d) 280 | dryrun=1 281 | ;; 282 | --conf-dir|-c) 283 | [ $# -ge 1 ] || fail "Missing arg to --conf-dir|-c" 284 | conf_dir="$1" 285 | shift 286 | ;; 287 | --help|-h) 288 | usage 289 | exit 290 | ;; 291 | --image|-i) 292 | [ $# -ge 1 ] || fail "Missing arg to --image|-i" 293 | eme_img="$1" 294 | shift 295 | ;; 296 | --email|-e) 297 | [ $# -ge 1 ] || fail "Missing arg to --email|-e" 298 | ssl_email="$1" 299 | shift 300 | ;; 301 | --no-email) 302 | no_email=1 303 | ;; 304 | --slot-key) 305 | [ $# -ge 1 ] || fail "Missing arg to --slot-key" 306 | slot_key="$1" 307 | shift 308 | ;; 309 | --non-interactive) 310 | non_interactive=1 311 | ;; 312 | --server-name) 313 | [ $# -ge 1 ] || fail "Missing arg to --server_name" 314 | server_name="$1" 315 | shift 316 | ;; 317 | --cert-type) 318 | [ $# -ge 1 ] || fail "Missing arg to --cert-type" 319 | cert_type="$1" 320 | shift 321 | [ "$cert_type" = 'comodo' ] \ 322 | && fail "TODO: implement using comodo" 323 | ;; 324 | --api-url) 325 | [ $# -ge 1 ] || fail "Missing arg to --api-url" 326 | api_url="$1" 327 | shift 328 | ;; 329 | --dns-check|-D) 330 | dns_check=1 331 | ;; 332 | --dns-test-ip) 333 | [ $# -ge 1 ] || fail "Missing arg to --dns-test-ip" 334 | dns_test_ip="$1" 335 | shift 336 | ;; 337 | --pull-image|-P) 338 | pull_image=1 339 | ;; 340 | --stats|-S) 341 | send_stats=1 342 | ;; 343 | --stats-server) 344 | [ $# -ge 1 ] || fail "Missing arg to --stats-server" 345 | stats_server="$1" 346 | shift 347 | ;; 348 | --stats-extra) 349 | stats_args="$stats_args --extra-node-information" 350 | ;; 351 | --stats-key) 352 | [ $# -ge 1 ] || fail "Missing arg to --stats-key" 353 | stats_args="$stats_args --auth-key $1" 354 | shift 355 | ;; 356 | --verbose|-v) 357 | verbose=1 358 | ;; 359 | --logging|-l) 360 | logging=1 361 | ;; 362 | --remote|-r) 363 | [ $arg_count -ne 0 ] && fail "If using --remote|-r it must be the first argument" 364 | [ $# -ge 1 ] || fail "Missing arg to --remote|-r" 365 | remote_host="$1" 366 | shift 367 | run_remote "$remote_host" "$@" 368 | exit $? 369 | ;; 370 | --restart|-R) 371 | restart=1 372 | ;; 373 | --tune-network|-T) 374 | tune_network=1 375 | ;; 376 | *) 377 | [ -n "$action" ] && fail "Invalid arg '$arg'; an action of '$action' was already given" 378 | action="$arg" 379 | ;; 380 | esac 381 | let arg_count+=1 382 | done 383 | 384 | 385 | # setup for run 386 | # -------------------------------------------------- 387 | # a few sanity checks 388 | [ $send_stats -eq 1 -a -z "$stats_server" ] \ 389 | && fail "A stats server (e.g. http://pep-stats.example.com) is required for --stats)" 390 | cmd which docker > /dev/null || fail "Docker is not installed" 391 | case "$action" in 392 | init|run|clean|reset|shell) 393 | ;; 394 | *) 395 | usage 396 | fail "Invalid action: '$action'" 397 | esac 398 | [ "$cert_type" = 'comodo' -o "$cert_type" = 'letsencrypt' ] \ 399 | || fail "Invalid certificate type: $cert_type" 400 | 401 | 402 | # pull the latest image down if requested 403 | [ $pull_image -eq 1 ] && { 404 | rem "pulling '$eme_img' from Docker Hub" 405 | cmd docker pull "$eme_img" \ 406 | || fail "Failed to pull Encrypt.me client image '$eme_img' from Docker Hub" 407 | } 408 | 409 | 410 | # init/run pre-tasks 411 | [ "$action" = 'init' -o "$action" = 'run' ] && { 412 | # now do all image images we need exist? 413 | eme_img_id=$(cmd docker images -q "$eme_img") 414 | [ -n "$eme_img_id" ] \ 415 | || fail "No docker image named '$eme_img' found; either build the image or use --pull-image to pull the image from Docker Hub" 416 | 417 | modinfo wireguard &> /dev/null || rem "Missed wireguard kernel module, therefore protocol is disabled" 418 | 419 | # get auth/server info if needed 420 | rem "interactively collecting any required missing params" 421 | collect_args 422 | } 423 | 424 | 425 | # perform the main action 426 | # -------------------------------------------------- 427 | [ "$action" = "init" ] && { 428 | rem "starting $name container to run config initialization" 429 | server_init || fail "Docker container and/or Encrypt.me private end-point failed to initialize" 430 | } 431 | 432 | [ "$action" = "run" ] && { 433 | rem "starting $name container" 434 | server_run || fail "Failed to start Docker container for Encrypt.me private end-point" 435 | } 436 | 437 | [ "$action" = "shell" ] && { 438 | rem "starting temporary container" 439 | server_shell 440 | } 441 | 442 | [ "$action" = "reset" ] && { 443 | rem "cleaning up traces of $name container and configs" 444 | server_reset 445 | } 446 | 447 | [ "$action" = "clean" ] && { 448 | rem "cleaning up all traces of $name container, images, and configs" 449 | server_cleanup 450 | } 451 | 452 | exit 0 453 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ux 2 | 3 | REG_KEY="${REG_KEY:-}" 4 | API_URL="${API_URL:-}" 5 | SSL_EMAIL="${SSL_EMAIL:-}" 6 | PEP_IMAGE="${PEP_IMAGE:-}" 7 | BRANCH="${BRANCH:-}" 8 | STATS_SERVER="${STATS_SERVER:-}" 9 | 10 | REMOTE_USER="${REMOTE_USER:-}" 11 | REMOTE_HOST="${REMOTE_HOST:-}" 12 | REMOTE_HOST_IP="${REMOTE_HOST_IP:-}" 13 | 14 | 15 | fail() { 16 | echo "! ${1:-error}" >&1 17 | exit ${2:-1} 18 | } 19 | 20 | usage() { 21 | cat <) 12 | - Replaced hard coded '/dev/null in __init__ with os.devnull 13 | - Added OS check to conditionally remove code that doesn't 14 | work on OS X 15 | - Added output to console on completion 16 | - Tidied up formatting 17 | 11th Mar 2009 (David Mytton ) 18 | - Fixed problem with daemon exiting on Python 2.4 19 | (before SystemExit was part of the Exception base) 20 | 13th Aug 2010 (David Mytton 21 | - Fixed unhandled exception if PID file is empty 22 | 20th Ot 2020 (Jonny Fillmore 23 | - minor updates for Python3.6+ 24 | ''' 25 | 26 | # Core modules 27 | import atexit 28 | import errno 29 | import os 30 | import signal 31 | import sys 32 | import time 33 | 34 | 35 | class Daemon: 36 | """ 37 | A generic daemon class. 38 | 39 | Usage: subclass the Daemon class and override the run() method 40 | """ 41 | def __init__(self, pidfile, stdin=os.devnull, 42 | stdout=os.devnull, stderr=os.devnull, 43 | home_dir='.', umask=0o22, verbose=1, 44 | use_gevent=False, use_eventlet=False): 45 | self.stdin = stdin 46 | self.stdout = stdout 47 | self.stderr = stderr 48 | self.pidfile = pidfile 49 | self.home_dir = home_dir 50 | self.verbose = verbose 51 | self.umask = umask 52 | self.daemon_alive = True 53 | self.use_gevent = use_gevent 54 | self.use_eventlet = use_eventlet 55 | 56 | def log(self, *args): 57 | if self.verbose >= 1: 58 | print(*args) 59 | 60 | def daemonize(self): 61 | """ 62 | Do the UNIX double-fork magic, see Stevens' "Advanced 63 | Programming in the UNIX Environment" for details (ISBN 0201563177) 64 | http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16 65 | """ 66 | if self.use_eventlet: 67 | import eventlet.tpool 68 | eventlet.tpool.killall() 69 | try: 70 | pid = os.fork() 71 | if pid > 0: 72 | # Exit first parent 73 | sys.exit(0) 74 | except OSError as e: 75 | sys.stderr.write( 76 | "fork #1 failed: %d (%s)\n" % (e.errno, e.strerror)) 77 | sys.exit(1) 78 | 79 | # Decouple from parent environment 80 | os.chdir(self.home_dir) 81 | os.setsid() 82 | os.umask(self.umask) 83 | 84 | # Do second fork 85 | try: 86 | pid = os.fork() 87 | if pid > 0: 88 | # Exit from second parent 89 | sys.exit(0) 90 | except OSError as e: 91 | sys.stderr.write( 92 | "fork #2 failed: %d (%s)\n" % (e.errno, e.strerror)) 93 | sys.exit(1) 94 | 95 | if sys.platform != 'darwin': # This block breaks on OS X 96 | # Redirect standard file descriptors 97 | sys.stdout.flush() 98 | sys.stderr.flush() 99 | si = open(self.stdin, 'r') 100 | so = open(self.stdout, 'a+') 101 | if self.stderr: 102 | try: 103 | se = open(self.stderr, 'a+', 0) 104 | except ValueError: 105 | # Python 3 can't have unbuffered text I/O 106 | se = open(self.stderr, 'a+', 1) 107 | else: 108 | se = so 109 | os.dup2(si.fileno(), sys.stdin.fileno()) 110 | os.dup2(so.fileno(), sys.stdout.fileno()) 111 | os.dup2(se.fileno(), sys.stderr.fileno()) 112 | 113 | def sigtermhandler(signum, frame): 114 | self.daemon_alive = False 115 | sys.exit() 116 | 117 | if self.use_gevent: 118 | import gevent 119 | gevent.reinit() 120 | gevent.signal(signal.SIGTERM, sigtermhandler, signal.SIGTERM, None) 121 | gevent.signal(signal.SIGINT, sigtermhandler, signal.SIGINT, None) 122 | else: 123 | signal.signal(signal.SIGTERM, sigtermhandler) 124 | signal.signal(signal.SIGINT, sigtermhandler) 125 | 126 | self.log("Started") 127 | 128 | # Write pidfile 129 | atexit.register( 130 | self.delpid) # Make sure pid file is removed if we quit 131 | pid = str(os.getpid()) 132 | open(self.pidfile, 'w+').write("%s\n" % pid) 133 | 134 | def delpid(self): 135 | try: 136 | # the process may fork itself again 137 | pid = int(open(self.pidfile, 'r').read().strip()) 138 | if pid == os.getpid(): 139 | os.remove(self.pidfile) 140 | except OSError as e: 141 | if e.errno == errno.ENOENT: 142 | pass 143 | else: 144 | raise 145 | 146 | def start(self, *args, **kwargs): 147 | """ 148 | Start the daemon 149 | """ 150 | 151 | self.log("Starting...") 152 | 153 | # Check for a pidfile to see if the daemon already runs 154 | try: 155 | pf = open(self.pidfile, 'r') 156 | pid = int(pf.read().strip()) 157 | pf.close() 158 | except IOError: 159 | pid = None 160 | except SystemExit: 161 | pid = None 162 | 163 | if pid: 164 | message = "pidfile %s already exists. Is it already running?\n" 165 | sys.stderr.write(message % self.pidfile) 166 | sys.exit(1) 167 | 168 | # Start the daemon 169 | self.daemonize() 170 | self.run(*args, **kwargs) 171 | 172 | def stop(self): 173 | """ 174 | Stop the daemon 175 | """ 176 | 177 | if self.verbose >= 1: 178 | self.log("Stopping...") 179 | 180 | # Get the pid from the pidfile 181 | pid = self.get_pid() 182 | 183 | if not pid: 184 | message = "pidfile %s does not exist. Not running?\n" 185 | sys.stderr.write(message % self.pidfile) 186 | 187 | # Just to be sure. A ValueError might occur if the PID file is 188 | # empty but does actually exist 189 | if os.path.exists(self.pidfile): 190 | os.remove(self.pidfile) 191 | 192 | return # Not an error in a restart 193 | 194 | # Try killing the daemon process 195 | try: 196 | i = 0 197 | while 1: 198 | os.kill(pid, signal.SIGTERM) 199 | time.sleep(0.1) 200 | i = i + 1 201 | if i % 10 == 0: 202 | os.kill(pid, signal.SIGHUP) 203 | except OSError as err: 204 | if err.errno == errno.ESRCH: 205 | if os.path.exists(self.pidfile): 206 | os.remove(self.pidfile) 207 | else: 208 | print(str(err)) 209 | sys.exit(1) 210 | 211 | self.log("Stopped") 212 | 213 | def restart(self): 214 | """ 215 | Restart the daemon 216 | """ 217 | self.stop() 218 | self.start() 219 | 220 | def get_pid(self): 221 | try: 222 | pf = open(self.pidfile, 'r') 223 | pid = int(pf.read().strip()) 224 | pf.close() 225 | except IOError: 226 | pid = None 227 | except SystemExit: 228 | pid = None 229 | return pid 230 | 231 | def is_running(self): 232 | pid = self.get_pid() 233 | 234 | if pid is None: 235 | self.log('Process is stopped') 236 | return False 237 | elif os.path.exists('/proc/%d' % pid): 238 | self.log('Process (pid %d) is running...' % pid) 239 | return True 240 | else: 241 | self.log('Process (pid %d) is killed' % pid) 242 | return False 243 | 244 | def run(self): 245 | """ 246 | You should override this method when you subclass Daemon. 247 | It will be called after the process has been 248 | daemonized by start() or restart(). 249 | """ 250 | raise NotImplementedError 251 | -------------------------------------------------------------------------------- /to_copy/opt/dns-filter/server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from contextlib import closing 4 | import socket 5 | import os 6 | import sys 7 | import grp 8 | import pwd 9 | import json 10 | 11 | import daemon 12 | 13 | 14 | # daemon configuration 15 | FILTERS_DIR = "/etc/encryptme/filters" 16 | SOCKET_PATH = "/usr/local/unbound-1.7/etc/unbound/dns_filter.sock" 17 | PID_FILE = "/usr/local/unbound-1.7/etc/unbound/dns-filter.pid" 18 | 19 | 20 | def delete_socket_path(socket_path): 21 | try: 22 | os.unlink(socket_path) 23 | except OSError: 24 | if os.path.exists(socket_path): 25 | raise 26 | 27 | 28 | class FilterList: 29 | def __init__(self, filters_dir): 30 | """ 31 | Build entries from file. 32 | """ 33 | self.disable_doh = False 34 | self.blacklist = set() 35 | self.load(filters_dir) 36 | 37 | @staticmethod 38 | def _yield_lines(path): 39 | with open(path) as list_file: 40 | for item in list_file: 41 | yield item.strip() 42 | 43 | def load(self, filters_dir): 44 | if not os.path.isdir(filters_dir): 45 | return 46 | for name in os.listdir(filters_dir): 47 | if not name.endswith('.domains.blacklist'): 48 | continue 49 | for domain in self._yield_lines(os.path.join(filters_dir, name)): 50 | self.blacklist.add(domain) 51 | self.disable_doh = bool(self.blacklist) 52 | 53 | def is_blocked(self, domain): 54 | ''' 55 | Returns whether this domain is blocked or is a sub-domain of a blocked 56 | domain. 57 | ''' 58 | name = domain 59 | while '.' in name: 60 | if name in self.blacklist: 61 | return True 62 | name = name[name.find('.') + 1:] 63 | return False 64 | 65 | 66 | class FilterDaemon(daemon.Daemon): 67 | def __init__(self, socket_path, filters_dir, **kwargs): 68 | self.socket_path = socket_path 69 | self.filters_dir = filters_dir 70 | super(FilterDaemon, self).__init__(**kwargs) 71 | 72 | def run(self): 73 | filter_list = FilterList(self.filters_dir) 74 | 75 | delete_socket_path(self.socket_path) 76 | 77 | #create the socket 78 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 79 | with closing(sock): 80 | sock.bind(self.socket_path) 81 | sock.listen(1) 82 | if not os.path.exists(self.filters_dir): 83 | os.makedirs(self.filters_dir) 84 | uid = pwd.getpwnam("unbound").pw_uid 85 | gid = grp.getgrnam("unbound").gr_gid 86 | os.chown(self.socket_path, uid, gid) 87 | self._run_loop(sock, filter_list) 88 | 89 | def _run_loop(self, sock, filter_list): 90 | while True: 91 | connection, address = sock.accept() 92 | with closing(connection): 93 | data = connection.recv(2048) 94 | if data: 95 | request = json.loads(data) 96 | response = [ 97 | filter_list.is_blocked(request['domain'].strip()), 98 | filter_list.disable_doh, 99 | ] 100 | connection.sendall(json.dumps(response).encode('utf-8')) 101 | 102 | 103 | if __name__ == "__main__": 104 | daemon = FilterDaemon( 105 | socket_path=SOCKET_PATH, 106 | filters_dir=FILTERS_DIR, 107 | pidfile=PID_FILE 108 | ) 109 | 110 | if len(sys.argv) != 2: 111 | print("Unknown command") 112 | sys.exit(2) 113 | 114 | if 'start' == sys.argv[1]: 115 | daemon.start() 116 | 117 | elif 'stop' == sys.argv[1]: 118 | daemon.stop() 119 | delete_socket_path(SOCKET_PATH) 120 | 121 | elif 'restart' == sys.argv[1]: 122 | daemon.stop() 123 | delete_socket_path(SOCKET_PATH) 124 | daemon.start() 125 | 126 | elif 'status' == sys.argv[1]: 127 | try: 128 | pf = file(PID_FILE, 'r') 129 | pid = int(pf.read().strip()) 130 | pf.close() 131 | except IOError: 132 | pid = None 133 | except SystemExit: 134 | pid = None 135 | if pid: 136 | print('dns-filter is running with pid %s' % pid) 137 | else: 138 | print('dns-filter is not running.') 139 | else: 140 | print("usage: %s start|stop|restart|status" % sys.argv[0]) 141 | sys.exit(2) 142 | -------------------------------------------------------------------------------- /to_copy/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ** The belly of the beast ** 4 | # 5 | # - registers with Encrypt.me and fetches configuration information 6 | # - stores everything in $ENCRYPTME_DIR 7 | # - setups up SSL, VPN daemons, etc 8 | # - configures network: uses 100.64.0.0/10 for client connections: 9 | # - openvpn udp = 100.64.0.0 > 100.64.63.255 (/18) 10 | # - openvpn tcp = 100.64.64.0 > 100.64.127.255 (/18) 11 | # - ipsec udp = 100.64.128.0 > 100.64.191.255 (/18) 12 | # - wireguard udp = 100.96.0.0 > 100.127.255.255 (/11) 13 | 14 | 15 | BASE_DIR=$(cd $(dirname "$0") && pwd -P) 16 | SCRIPT_NAME=$(basename "$0") 17 | 18 | # setting this avoids unnecessary use of surrogates that can break server registration 19 | export LANG=en_US.UTF-8 20 | 21 | # main conf opts 22 | ENCRYPTME_DIR="${ENCRYPTME_DIR:-/etc/encryptme}" 23 | ENCRYPTME_API_URL="${ENCRYPTME_API_URL:-}" 24 | ENCRYPTME_CONF="${ENCRYPTME_DIR}/encryptme.conf" 25 | ENCRYPTME_SYSCTL_CONF="/etc/sysctl.d/encryptme.conf" 26 | ENCRYPTME_PKI_DIR="${ENCRYPTME_PKI_DIR:-$ENCRYPTME_DIR/pki}" 27 | ENCRYPTME_DATA_DIR="${ENCRYPTME_DATA_DIR:-$ENCRYPTME_DIR/data}" 28 | # stats opts 29 | ENCRYPTME_STATS="${ENCRYPTME_STATS:-0}" 30 | ENCRYPTME_STATS_SERVER="${ENCRYPTME_STATS_SERVER:-}" 31 | ENCRYPTME_STATS_ARGS="${ENCRYPTME_STATS_ARGS:-}" 32 | # helper run-time opts 33 | DNS_TEST_IP=${DNS_TEST_IP:-} 34 | # ssl opts 35 | LETSENCRYPT_DISABLED=${LETSENCRYPT_DISABLED:-0} 36 | LETSENCRYPT_STAGING=${LETSENCRYPT_STAGING:-0} 37 | SSL_EMAIL=${SSL_EMAIL:-} 38 | NO_EMAIL=${NO_EMAIL:-0} 39 | ENCRYPTME_TUNE_NETWORK=${ENCRYPTME_TUNE_NETWORK:-} 40 | # misc opts 41 | VERBOSE=${ENCRYPTME_VERBOSE:-0} 42 | DNS_FILTER_PID_FILE="/usr/local/unbound-1.7/etc/unbound/dns-filter.pid" 43 | CERT_SESSION_MAP="${ENCRYPTME_DATA_DIR}/cert_session_map" 44 | WG_IFACE=${WG_IFACE:-wg0} 45 | 46 | 47 | # helpers 48 | # ---------------------------------------------------------------------------- 49 | fail() { 50 | echo "${1:-failed}" >&1 51 | exit ${2:-1} 52 | } 53 | 54 | cmd() { 55 | [ $VERBOSE -gt 0 ] && echo "$ $@" >&2 56 | "$@" 57 | } 58 | 59 | rem() { 60 | [ $VERBOSE -gt 0 ] && echo "# $@" >&2 61 | return 0 62 | } 63 | 64 | rundaemon () { 65 | if (( $(ps -ef | grep -v grep | grep $1 | wc -l) == 0 )); then 66 | rem "starting" "$@" 67 | "$@" 68 | fi 69 | } 70 | 71 | encryptme_server() { 72 | local args=(--config $ENCRYPTME_CONF "$@") 73 | local cont_ver= 74 | if [ -n "$ENCRYPTME_API_URL" ]; then 75 | args=(--base_url "$ENCRYPTME_API_URL" "${args[@]}") 76 | fi 77 | [ -f '/container-version-id' ] && { 78 | cont_ver=$(/dev/null | grep -E '^(A|CNAME) ' | tail -1) 91 | while echo $dns_resp | grep -q '^CNAME'; do 92 | dns_resp=$(dig +short +trace "$(echo "$dns_resp" | awk '{print $2}')" 2>/dev/null | grep -E '^(A|CNAME) ' | tail -1) 93 | let cnames+=1 94 | [ $cnames -ge 10 ] && fail "More than 10 levels of cname redirection; loop detected?" 95 | done 96 | 97 | if [ -z $dns_resp ] 98 | then 99 | dns_resp=$(dig +short "$host" 2>/dev/null | tail -1) 100 | [ -n $dns_resp ] && dns_resp="A $dns_resp" 101 | fi 102 | 103 | [ -n "$dns_resp" ] || fail "Unable to resolve IP from server name" 104 | 105 | echo "$dns_resp" 106 | } 107 | 108 | 109 | # VPN protocol setup 110 | # ---------------------------------------------------------------------------- 111 | setup_wireguard() { 112 | local ip_addr="$1" 113 | local conf="$ENCRYPTME_DATA_DIR/server.json" 114 | local dirty=0 115 | local wg_refresh_args=('wg_iface=wg0') 116 | # generate keys and initial configs 117 | modprobe wireguard || fail "Failed to load WireGuard kernel module" # ensure kernel model is loaded 118 | mkdir -p "$ENCRYPTME_DIR/wireguard/keys" 119 | ( 120 | [ -d /etc/wireguard ] && rmdir /etc/wireguard &>/dev/null 121 | cd /etc && ln -sf "$ENCRYPTME_DIR/wireguard" 122 | cd "$ENCRYPTME_DIR/wireguard" 123 | # ensure our files are not readable by others 124 | umask 077 125 | [ -s keys/private -a -s keys/public ] || { 126 | wg genkey | tee keys/private | wg pubkey | tee keys/public 127 | dirty=1 128 | } 129 | # if we got a new key or we have no config at all... write one 130 | [ $dirty -eq 1 -o \! -s $WG_IFACE.conf ] && { 131 | cat > $WG_IFACE.conf </dev/null && ip link delete "$WG_IFACE" 140 | wg-quick up "$ENCRYPTME_DIR/wireguard/$WG_IFACE.conf" 141 | ) || fail "Failed to setup wireguard" 142 | # register our public key 143 | encryptme_server update --wireguard-public-key $(<"$ENCRYPTME_DIR/wireguard/keys/public") \ 144 | || fail "Failed to register WireGuard public key" 145 | # set peer configuration information based on authorized users/devices 146 | if [ -n "$ENCRYPTME_API_URL" ]; then 147 | wg_refresh_args+=("base_url=$ENCRYPTME_API_URL") 148 | fi 149 | refresh-wireguard.py "${wg_refresh_args[@]}" \ 150 | || fail "Failed to set initial WireGuard peers" 151 | } 152 | 153 | 154 | # start of the fun! 155 | # ---------------------------------------------------------------------------- 156 | # debug mode, if requested 157 | [ $VERBOSE -gt 0 ] && set -x 158 | 159 | # sanity checks and basic init 160 | if [ ! -d "$ENCRYPTME_DIR" ]; then 161 | fail "ENCRYPTME_DIR '$ENCRYPTME_DIR' must exist" 1 162 | elif [ ! -w "$ENCRYPTME_DIR" ]; then 163 | fail "ENCRYPTME_DIR '$ENCRYPTME_DIR' must be writable" 2 164 | fi 165 | cmd mkdir -p "$ENCRYPTME_PKI_DIR"/crls 166 | if [ ! -d "$ENCRYPTME_PKI_DIR" ]; then 167 | fail "ENCRYPTME_PKI_DIR '$ENCRYPTME_PKI_DIR' did not exist and count not be created" 3 168 | fi 169 | if [ "$NO_EMAIL" = 0 -a -z "$SSL_EMAIL" -a "$LETSENCRYPT_DISABLED" != 1 ]; then 170 | fail "SSL_EMAIL must be set if LETSENCRYPT_DISABLED is not set" 4 171 | fi 172 | if [ "$ENCRYPTME_STATS" = 1 -a -z "$ENCRYPTME_STATS_SERVER" ]; then 173 | fail "ENCRYPTME_STATS=1 but no ENCRYPTME_STATS_SERVER" 174 | fi 175 | 176 | cmd mkdir -p "$ENCRYPTME_DATA_DIR" \ 177 | || fail "Failed to create Encrypt.me data dir '$ENCRYPTME_DATA_DIR'" 5 178 | 179 | touch $CERT_SESSION_MAP || fail "Failed to create cert_session_map" 180 | 181 | # Inside the container creates /etc/sysctl.d/encryptme.conf with sysctl.conf tuning params. 182 | if [ "$ENCRYPTME_TUNE_NETWORK" = 1 ]; then 183 | touch $ENCRYPTME_SYSCTL_CONF || fail "Failed to create encryptme.conf" 184 | cat > $ENCRYPTME_SYSCTL_CONF << EOF 185 | net.core.somaxconn=1024 186 | net.core.netdev_max_backlog=250000 187 | net.core.rmem_default=262144 188 | net.core.rmem_max=16777216 189 | net.core.wmem_default=262144 190 | net.core.wmem_max=16777216 191 | net.ipv4.tcp_rmem=262144 262144 16777216 192 | net.ipv4.tcp_wmem=262144 262144 16777216 193 | net.ipv4.tcp_max_syn_backlog=1000 194 | net.ipv4.tcp_slow_start_after_idle=0 195 | net.core.optmem_max=16777216 196 | net.netfilter.nf_conntrack_max=1008768 197 | EOF 198 | # Load sysctl encryptme.conf 199 | sysctl --load=$ENCRYPTME_SYSCTL_CONF 200 | fi 201 | 202 | # Run an configured Encrypt.me private end-point server (must have run 'config' first) 203 | 204 | set -eo pipefail 205 | 206 | 207 | case "$1" in 208 | /*) 209 | exec "$@" 210 | ;; 211 | bash*) 212 | exec "$@" 213 | ;; 214 | esac 215 | 216 | # register the server 217 | if [ -f "$ENCRYPTME_CONF" ]; then 218 | rem "Instance is already registered; skipping" >&2 219 | else 220 | opt_ENCRYPTME_SLOT_KEY=--key 221 | opt_ENCRYPTME_SERVER_NAME=--name 222 | args="" 223 | missing="" 224 | set "" 225 | for var in ENCRYPTME_SLOT_KEY \ 226 | ENCRYPTME_SERVER_NAME; do 227 | value="${!var}" 228 | if [ -z "$value" ]; then 229 | missing="$missing $var" 230 | else 231 | arg_var_name="opt_$var" 232 | set - "$@" "${!arg_var_name}" "$value" 233 | fi 234 | done 235 | shift 236 | 237 | if [ ! -t 1 -a ! -z "$missing" ]; then 238 | fail "Not on a TTY and missing env vars: $missing" 3 239 | fi 240 | 241 | # creates encryptme.conf 242 | if encryptme_server register "$@"; then 243 | rem "Registered" 244 | else 245 | fail "Registration failed" 4 246 | fi 247 | set - 248 | shift 249 | fi 250 | 251 | 252 | # request certificate approval 253 | if [ -f "$ENCRYPTME_PKI_DIR/cloak.pem" ]; then 254 | rem "Private key is already generated" 255 | else 256 | rem "Requesting certificate (and waiting for approval)" 257 | encryptme_server req --key "$ENCRYPTME_PKI_DIR/cloak.pem" 258 | fi 259 | 260 | 261 | # download PKI certificates 262 | if [ -f "$ENCRYPTME_PKI_DIR/crls.pem" ]; then 263 | rem "PKI certificates are already downloaded." 264 | else 265 | rem "Requesting approval for PKI certs" 266 | encryptme_server pki --force --out "$ENCRYPTME_PKI_DIR" --wait 267 | rem "Downloading PKI certs" 268 | encryptme_server crls --infile "$ENCRYPTME_PKI_DIR/crl_urls.txt" \ 269 | --out "$ENCRYPTME_PKI_DIR/crls" \ 270 | --format pem \ 271 | --post-hook "cat '$ENCRYPTME_PKI_DIR'/crls/*.pem > '$ENCRYPTME_PKI_DIR/crls.pem'" 272 | fi 273 | 274 | 275 | # ensure we have DH params generated 276 | if [ ! -f "$ENCRYPTME_PKI_DIR/dh2048.pem" ]; then 277 | rem "Generating DH Params" 278 | if [ ! -f /etc/dh2048.pem ]; then 279 | openssl dhparam -out "$ENCRYPTME_PKI_DIR/dh2048.pem" 2048 280 | else 281 | cp /etc/dh2048.pem "$ENCRYPTME_PKI_DIR/dh2048.pem" 282 | fi 283 | fi 284 | 285 | 286 | # Symlink certificates and keys to ipsec.d directory 287 | if [ ! -L "/etc/strongswan/ipsec.d/certs/cloak.pem" ]; then 288 | ln -s "$ENCRYPTME_PKI_DIR/crls.pem" "/etc/strongswan/ipsec.d/crls/crls.pem" 289 | ln -s "$ENCRYPTME_PKI_DIR/anchor.pem" "/etc/strongswan/ipsec.d/cacerts/cloak-anchor.pem" 290 | ln -s "$ENCRYPTME_PKI_DIR/client_ca.pem" "/etc/strongswan/ipsec.d/cacerts/cloak-client-ca.pem" 291 | ln -s "$ENCRYPTME_PKI_DIR/server.pem" "/etc/strongswan/ipsec.d/certs/cloak.pem" 292 | ln -s "$ENCRYPTME_PKI_DIR/cloak.pem" "/etc/strongswan/ipsec.d/private/cloak.pem" 293 | fi 294 | 295 | 296 | # Gather server/config information (e.g. FQDNs, open VPN settings) 297 | rem "Getting server info" 298 | encryptme_server info --json 299 | encryptme_server info --json | jq -M '.' | tee "$ENCRYPTME_DATA_DIR/server.json" 300 | if [ ! -s "$ENCRYPTME_DATA_DIR/server.json" ]; then 301 | fail "Failed to get or parse server 'info' API response" 5 302 | fi 303 | 304 | jq -r '.target.ikev2[].fqdn, .target.openvpn[].fqdn' \ 305 | < "$ENCRYPTME_DATA_DIR/server.json" \ 306 | | sort -u > "$ENCRYPTME_DATA_DIR/fqdns" 307 | FQDNS=$(cat "$ENCRYPTME_DATA_DIR/fqdns") || fail "Failed to fetch FQDNS" 308 | FQDN=${FQDNS%% *} 309 | 310 | 311 | # Test FQDNs match IPs on this system 312 | # TODO: ensure this to be reliable on DO and AWS 313 | # TODO: Note this is only valid for AWS http://169.254.169.254 is at Amazon 314 | DNSOK=1 315 | DNS=0.0.0.0 316 | if [ ${DNS_CHECK:-0} -ne 0 ]; then 317 | EXTIP=$(curl --connect-timeout 5 -s http://169.254.169.254/latest/meta-data/public-ipv4) 318 | for hostname in $FQDNS; do 319 | rem "Checking DNS for FQDN '$hostname'" 320 | DNS=`dig +short A $hostname | egrep '^[0-9]+\.'` 321 | if [ ! -z "$DNS" ]; then 322 | rem "Found IP '$DNS' for $hostname" 323 | if ip addr show | grep "$DNS" > /dev/null; then 324 | rem "Looks good: Found IP '$DNS' on local system" 325 | elif [ "$DNS" == "$EXTIP" ]; then 326 | rem "Looks good: '$DNS' matches with external IP of `hostname`" 327 | else 328 | DNSOK=0 329 | rem "WARNING: Could not find '$DNS' on the local system. DNS mismatch?" 330 | fi 331 | else 332 | rem "WARNING: $hostname does not resolve" 333 | DNSOK=0 334 | fi 335 | done 336 | fi 337 | 338 | # make sure the domain is resolving to us properly 339 | if [ -n "$DNS_TEST_IP" ]; then 340 | rem "Verifying $FQDN resolves to $DNS_TEST_IP" 341 | tries=0 342 | cnames=0 343 | fqdn_pointed=0 344 | # try up to 2 minutes for it to work 345 | while [ $tries -lt 12 -a $fqdn_pointed -eq 0 ]; do 346 | dns_resp=$(query_a "$FQDN") 347 | echo "$dns_resp" | grep "^A $DNS_TEST_IP " && fqdn_pointed=1 || sleep 10 348 | let tries+=1 349 | done 350 | [ $fqdn_pointed -eq 0 ] && fail "The FQDN '$FQDN' is still not pointed correctly" 351 | fi 352 | # and finally capture our public IP address for configs 353 | ip_addr=$(query_a "$FQDN" | awk '{print $2}') 354 | 355 | 356 | # Perform letsencrypt if not disabled 357 | # Also runs renewals if a cert exists 358 | LETSENCRYPT=0 359 | if [ "$LETSENCRYPT_DISABLED" = 0 ]; then 360 | LETSENCRYPT=1 361 | if [ "$DNSOK" = 0 ]; then 362 | rem "WARNING: DNS issues found, it is unlikely letsencrypt will succeed." 363 | fi 364 | # build up the letsencrypt args 365 | LE_ARGS=( 366 | --non-interactive 367 | --agree-tos 368 | certonly 369 | ) 370 | if [ "$NO_EMAIL" = 1 ]; then 371 | LE_ARGS=("${LE_ARGS[@]}" --register-unsafely-without-email) 372 | else 373 | LE_ARGS=("${LE_ARGS[@]}" --email "$SSL_EMAIL") 374 | fi 375 | for fqdn in $FQDNS; do 376 | LE_ARGS=("${LE_ARGS[@]}" -d $fqdn) 377 | 378 | # look for out-of-date LetsEncrypt configs and remove them so we can get a fresh data 379 | config_file="/etc/letsencrypt/renewal/$fqdn.conf" 380 | grep -q "^standalone_supported_challenges" "$config_file" 2>/dev/null && { 381 | rm -f "$config_file" 382 | rm -rf "/etc/letsencrypt/archive/$fqdn" 383 | rm -rf "/etc/letsencrypt/live/$fqdn" 384 | } 385 | done 386 | 387 | if [ "${LETSENCRYPT_STAGING:-}" = 1 ]; then 388 | LE_ARGS=("${LE_ARGS[@]}" --staging) 389 | fi 390 | LE_ARGS=( 391 | "${LE_ARGS[@]}" 392 | --expand 393 | --standalone 394 | --preferred-challenges 395 | http-01 396 | ) 397 | 398 | # temporarily allow in HTTP traffic to perform domain verification; need to insert, not append 399 | /sbin/iptables -I INPUT -p tcp --dport http -j ACCEPT 400 | ( 401 | if [ ! -f "/etc/letsencrypt/live/$FQDN/fullchain.pem" ]; then 402 | rem "Getting certificate for $FQDN" 403 | rem "Letsencrypt arguments: " "$@" 404 | # we get 5 failures per hostname per hour, so we gotta make it count 405 | tries=0 406 | success=0 407 | while [ $tries -lt 2 -a $success -eq 0 ]; do 408 | letsencrypt "${LE_ARGS[@]}" && success=1 || { 409 | let tries+=1 410 | sleep 60 411 | } 412 | done 413 | [ $success -eq 1 ] \ 414 | || fail "Failed to obtain LetsEncrypt SSL certificate." 415 | else 416 | letsencrypt renew 417 | fi 418 | ) 419 | success=$? 420 | /sbin/iptables -D INPUT -p tcp --dport http -j ACCEPT 421 | [ $success -eq 0 ] || fail "LetsEncrypt certificate management failed" 422 | 423 | cp "/etc/letsencrypt/live/$FQDN/privkey.pem" \ 424 | /etc/strongswan/ipsec.d/private/letsencrypt.pem \ 425 | || fail "Failed to copy privkey.pem to IPSec config dir" 426 | cp "/etc/letsencrypt/live/$FQDN/fullchain.pem" \ 427 | /etc/strongswan/ipsec.d/certs/letsencrypt.pem \ 428 | || fail "Failed to copy letsencrypt.pem to IPSec config dir" 429 | cp "/etc/letsencrypt/live/$FQDN/chain.pem" \ 430 | /etc/strongswan/ipsec.d/cacerts/letsencrypt.pem \ 431 | || fail "Failed to copy letsencrypt chain.pem to IPSec config dir" 432 | 433 | fi 434 | 435 | 436 | # Start services 437 | chmod -R 644 /etc/cron.d/* 438 | 439 | if [ -x /usr/sbin/crond ]; then 440 | rundaemon crond 441 | else 442 | rundaemon cron 443 | fi 444 | 445 | 446 | # Prevent PKI information from being read by non-privileged users 447 | chmod -R 700 "$ENCRYPTME_DIR/pki/" 448 | 449 | # Ensure networking is setup properly 450 | sysctl -w net.ipv4.ip_forward=1 451 | 452 | # IPsec should load required modules itself 453 | ## IPsec needs various modules loaded from host 454 | #for mod in ah4 ah6 esp4 esp6 xfrm4_tunnel xfrm6_tunnel xfrm_user \ 455 | # ip_tunnel xfrm4_mode_tunnel xfrm6_mode_tunnel \ 456 | # pcrypt xfrm_ipcomp deflate; do 457 | # modprobe $mod; 458 | #done 459 | 460 | # generate IP tables rules 461 | rem "Configuring IPTables, as needed" 462 | /bin/template.py \ 463 | -d "$ENCRYPTME_DATA_DIR/server.json" \ 464 | -s /etc/iptables.eme.rules.j2 \ 465 | -o "$ENCRYPTME_DIR/iptables.eme.rules" \ 466 | -v ipaddress=$DNS 467 | 468 | # play nicely with existing rules: if our chain is already present do nothing 469 | /sbin/iptables -L ENCRYPTME &>/dev/null || { 470 | rem "Configuring the ENCRYPTME chain" 471 | # merge host rules w/ our own 472 | /sbin/iptables-save > "$ENCRYPTME_DIR/iptables.host.rules" 473 | cat "$ENCRYPTME_DIR/iptables.host.rules" "$ENCRYPTME_DIR/iptables.eme.rules" \ 474 | > "$ENCRYPTME_DIR/iptables.rules" 475 | /sbin/iptables-restore --noflush "$ENCRYPTME_DIR/iptables.rules" 476 | # prune dupes, except for 'COMMIT' lines 477 | /sbin/iptables-save | awk '/^COMMIT$/ { delete x; }; !x[$0]++' | /sbin/iptables-restore 478 | } 479 | 480 | 481 | rem "Configuring and launching OpenVPN" 482 | OPENVPN_LOGLEVEL=0 483 | OPENVPN_LOG_OPT="" # Disabled in template 484 | [ "${ENCRYPTME_LOGGING:-}" = 1 ] && OPENVPN_LOGLEVEL=2 && 485 | OPENVPN_LOG_OPT="--syslog" 486 | 487 | get_openvpn_conf() { 488 | out=$(cat "$ENCRYPTME_DATA_DIR/server.json" | jq ".target.openvpn[$1]") 489 | if [ "$out" = null ]; then 490 | echo "" 491 | else 492 | echo "$out" 493 | fi 494 | } 495 | n=0 496 | conf="$(get_openvpn_conf $n)" 497 | while [ ! -z "$conf" ]; do 498 | echo "$conf" > "$ENCRYPTME_DATA_DIR/openvpn.$n.json" 499 | /bin/template.py \ 500 | -d "$ENCRYPTME_DATA_DIR/openvpn.$n.json" \ 501 | -x "$ENCRYPTME_DATA_DIR/server.json" \ 502 | -s /etc/openvpn/openvpn.conf.j2 \ 503 | -o /etc/openvpn/server-$n.conf \ 504 | -v logging=$ENCRYPTME_LOGGING 505 | rem "Started OpenVPN instance #$n" 506 | mkdir -p /var/run/openvpn 507 | test -e /var/run/openvpn/server-0.sock || \ 508 | mkfifo /var/run/openvpn/server-0.sock 509 | # if the params change we MUST update /usr/bin/reload-certficiate.sh 510 | rundaemon /usr/sbin/openvpn \ 511 | $OPENVPN_LOG_OPT \ 512 | --status /var/run/openvpn/server-$n.status 10 \ 513 | --cd /etc/openvpn \ 514 | --script-security 2 \ 515 | --config /etc/openvpn/server-$n.conf \ 516 | --writepid /var/run/openvpn/server-$n.pid \ 517 | --management /var/run/openvpn/server-$n.sock unix \ 518 | --verb $OPENVPN_LOGLEVEL \ 519 | & 520 | n=$[ $n + 1 ] 521 | conf="$(get_openvpn_conf $n)" 522 | done 523 | 524 | 525 | STRONGSWAN_LOGLEVEL=-1 526 | [ "${ENCRYPTME_LOGGING:-}" = 1 ] && STRONGSWAN_LOGLEVEL=2 527 | 528 | rem "Configuring and starting strongSwan" 529 | 530 | /bin/template.py \ 531 | -d "$ENCRYPTME_DATA_DIR/server.json" \ 532 | -s /etc/strongswan/ipsec.conf.j2 \ 533 | -o /etc/strongswan/ipsec.conf \ 534 | -v letsencrypt=$LETSENCRYPT 535 | 536 | /bin/template.py \ 537 | -d "$ENCRYPTME_DATA_DIR/server.json" \ 538 | -s /etc/strongswan/ipsec.secrets.j2 \ 539 | -o /etc/strongswan/ipsec.secrets \ 540 | -v letsencrypt=$LETSENCRYPT 541 | 542 | /bin/template.py \ 543 | -d "$ENCRYPTME_DATA_DIR/server.json" \ 544 | -s /etc/strongswan/strongswan.conf.j2 \ 545 | -o /etc/strongswan/strongswan.conf \ 546 | -v loglevel=$STRONGSWAN_LOGLEVEL 547 | 548 | # Windows now requires some extra certs due to changes Let's Encrypt is making 549 | # or ipsec connections fail :( 550 | # https://letsencrypt.org/docs/dst-root-ca-x3-expiration-september-2021/ 551 | cat > /etc/strongswan/ipsec.d/cacerts/lets-encrypt-r3.crt < /etc/strongswan/ipsec.d/cacerts/dst-root-ca-x3.crt </dev/null; then 622 | setup_wireguard "$ip_addr" 623 | else 624 | rem "Missed WireGuard kernel module" 625 | fi 626 | 627 | 628 | [ ${INIT_ONLY:-0} = "1" ] && { 629 | rem "Init complete; run './go.sh run' to start" 630 | exit 0 631 | } 632 | 633 | 634 | # preemptively see if we have a new certificate for some reason so we're not 635 | # waiting on a cronjob to figure it out 636 | /usr/bin/update-pki.sh 637 | 638 | [ $ENCRYPTME_STATS = 1 -a -n "$ENCRYPTME_STATS_SERVER" ] && { 639 | rem "Starting statistics gatherer, sending to $ENCRYPTME_STATS_SERVER" 640 | encryptme-stats --server "$ENCRYPTME_STATS_SERVER" $ENCRYPTME_STATS_ARGS & 641 | } 642 | 643 | 644 | # the DNS filter must be running before unbound 645 | [ -f "$DNS_FILTER_PID_FILE" ] && rm "$DNS_FILTER_PID_FILE" 646 | rem "Restoring content-type filters and starting filter server" 647 | /usr/bin/pep-filter.sh reload 648 | 649 | 650 | PYTHONPATH="$PYTHONPATH:/usr/local/unbound-1.7/etc/unbound/usr/lib64/python2.7/site-packages" \ 651 | rundaemon /usr/local/unbound-1.7/sbin/unbound -d \ 652 | -c /usr/local/unbound-1.7/etc/unbound/unbound.conf & 653 | 654 | 655 | # since we don't have a good single foreground process... we just spin! 656 | rem "Start-up complete" 657 | while true; do 658 | sleep 300 659 | done 660 | -------------------------------------------------------------------------------- /to_copy/usr/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/encryptme/private-end-points-docker/908dce50356faadd9b4f6c9435b4ccf69e336650/to_copy/usr/__init__.py -------------------------------------------------------------------------------- /to_copy/usr/bin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/encryptme/private-end-points-docker/908dce50356faadd9b4f6c9435b4ccf69e336650/to_copy/usr/bin/__init__.py -------------------------------------------------------------------------------- /to_copy/usr/bin/boot-cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 4 | export PATH 5 | 6 | 7 | fail() { 8 | echo "${1-command failed}" >&2 9 | exit ${2:-1} 10 | } 11 | 12 | 13 | for socket in "/var/run/openvpn/*.sock"; do 14 | OPENVPN_CONNECTION=$(echo status | socat - UNIX-CONNECT:/var/run/openvpn/server-0.sock | grep "$1") 15 | [ -n "$OPENVPN_CONNECTION" ] && break 16 | done 17 | 18 | IPSEC_CONNECTION=$(ipsec status | grep "$1" | sed -r 's/.*cloak\[([[:digit:]]+)\].+/\1/g') 19 | 20 | if [ -n "$IPSEC_CONNECTION" ]; then 21 | echo "killing ipsec connection $1" 22 | ipsec down cloak[$IPSEC_CONNECTION] || fail "Could not kill ipsec connection" "1" 23 | fi 24 | 25 | if [ -n "$OPENVPN_CONNECTION" ]; then 26 | echo "Killing openvpn connection $1 ($OPENVPN_CONNECTION)" 27 | echo "kill '$1'" | socat - UNIX-CONNECT:/var/run/openvpn/server-0.sock \ 28 | || fail "Could not kill openvpn connection" "1" 29 | fi -------------------------------------------------------------------------------- /to_copy/usr/bin/boot-expired-revoked-certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | LOCKFILE="/etc/encryptme/data/.cert_lock" 4 | SESSION_MAP=/etc/encryptme/data/cert_session_map 5 | URL_FILE=/etc/encryptme/pki/crl_urls.txt 6 | CRL_LIST_FILE=/tmp/crl.list 7 | IPSEC_CERT_INFO=/tmp/ipsec_cert_info 8 | REVOKED_CERTS=/tmp/revoked_certs 9 | 10 | 11 | fail() { 12 | echo "${1-command failed}" >&2 13 | [ -f $LOCKFILE ] && rm -f $LOCKFILE 14 | exit ${2:-1} 15 | } 16 | 17 | 18 | get_revoked_certs() { 19 | xargs -n 1 curl -s -o $CRL_LIST_FILE < $URL_FILE 20 | 21 | openssl crl -inform DER -text -noout -in $CRL_LIST_FILE \ 22 | | grep "Serial Number" | sed 's/.*Serial Number: //g' > $REVOKED_CERTS 23 | } 24 | 25 | 26 | get_ipsec_cert_info() { 27 | rm -f $IPSEC_CERT_INFO 28 | touch $IPSEC_CERT_INFO 29 | touch $SESSION_MAP 30 | ipsec listcerts | while read line; do 31 | output=$(echo "$line" | grep "subject:" | sed 's/.*subject: //g' | tr -d '"' ) 32 | [ -n "$output" ] && { 33 | record="$output" 34 | } 35 | 36 | output=$(echo "$line" | grep "not after") 37 | [ -n "$output" ] && { 38 | output=$(echo "$line" | grep "expired" ) 39 | [ -n "$output" ] && record="$record;EXPIRED" || record="$record;" 40 | } 41 | 42 | serial=$(echo "$line" | grep "serial:" | sed 's/.*serial: //g' | tr -d ':' ) 43 | [ -n "$serial" ] && { 44 | serial="${serial^^}" 45 | record="$record;$serial" 46 | grep "$serial" $REVOKED_CERTS && record="$record;REVOKED" 47 | } 48 | 49 | output=$(echo "$line" | grep "flags:" | sed 's/.*flags: //g') 50 | if [ "$output" = "clientAuth" ]; then 51 | echo "$record" >> $IPSEC_CERT_INFO 52 | fi 53 | done 54 | } 55 | 56 | 57 | kill_session() { 58 | local session="$1" 59 | local openvpn_type="${2:-0}" 60 | 61 | echo "killing session: $session" 62 | sh /usr/bin/boot-cert.sh "$session" 63 | [ $? -gt 0 ] && fail "Could not kill the session" 64 | 65 | if [ "$openvpn_type" -gt 0 ]; then 66 | ## Remove it from the cert session 67 | count=0 68 | while [ -f "$LOCKFILE" ]; do 69 | sleep 1 70 | ((count++)) 71 | [ $count -gt 5 ] && fail "Could not get a lock" 72 | done 73 | 74 | echo "$$" > $LOCKFILE 75 | grep -v "$session" $SESSION_MAP > /tmp/tmp_session_map 76 | mv -f /tmp/tmp_session_map $SESSION_MAP 77 | rm -f $LOCKFILE 78 | fi 79 | } 80 | 81 | 82 | terminate_expired_certs() { 83 | cat $SESSION_MAP | while read line; do 84 | end_date=$( echo "$line" | cut -d ',' -f 6 ) 85 | if [ -n "$end_date" ]; then 86 | now_epoch=$( date +%s ) 87 | end_date_epoch=$( date -d "$end_date" +%s ) 88 | if [[ $now_epoch > $end_date_epoch ]]; then 89 | session=$( echo "$line" | cut -d ',' -f 1 ) 90 | kill_session "$session" 1 91 | fi 92 | fi 93 | done 94 | 95 | grep "EXPIRED" $IPSEC_CERT_INFO | while read -r line; do 96 | subject=$(echo "$line" | cut -d ';' -f 1 ) 97 | if [ -n "$subject" ]; then 98 | valid=$(grep "$subject" $IPSEC_CERT_INFO | grep -v "EXPIRED") 99 | [ -z "$valid" ] && { 100 | echo "EXPIRED" 101 | kill_session "$subject" 102 | } 103 | fi 104 | done 105 | } 106 | 107 | 108 | terminate_revoked_certs() { 109 | cat $REVOKED_CERTS | while read -r line ; do 110 | # If it exists in the session file kill it 111 | session=$(cat $SESSION_MAP | grep "$line" | awk '{split($0,a,","); print a[1]}') 112 | if [ -n "$session" ]; then 113 | kill_session "$session" 1 114 | fi 115 | done 116 | 117 | grep 'REVOKED' $IPSEC_CERT_INFO | while read -r line ; do 118 | subject=$(echo "$line" | cut -d ';' -f 1 ) 119 | if [ -n "$subject" ]; then 120 | valid=$(grep "$subject" $IPSEC_CERT_INFO | grep -v "REVOKED") 121 | [ -z "$valid" ] && { 122 | echo REVOKED 123 | kill_session "$subject" 124 | } 125 | fi 126 | done 127 | 128 | } 129 | 130 | 131 | get_revoked_certs 132 | get_ipsec_cert_info 133 | terminate_expired_certs 134 | terminate_revoked_certs 135 | 136 | -------------------------------------------------------------------------------- /to_copy/usr/bin/obtain-cert-end-date.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | # An OpenVPN tls-verify script 4 | # 5 | # This would obtain certificate's end date of a pending TLS connection. 6 | 7 | 8 | LOCKFILE="/etc/encryptme/data/.cert_lock" 9 | END_DATE_FILE="/etc/encryptme/data/cert_end_date" 10 | 11 | fail() { 12 | echo "${1-command failed}" >&2 13 | [ -f "$LOCKFILE" ] && rm $LOCKFILE 14 | exit ${2:-1} 15 | } 16 | 17 | depth="$1" 18 | subject="$2" 19 | 20 | count=0 21 | while [ -f "$LOCKFILE" ]; do 22 | sleep 1 23 | ((count++)) 24 | [ $count -gt 5 ] && fail "Could not get a lock" 25 | done 26 | echo "$$" > $LOCKFILE 27 | 28 | # Only use the end-entity certificate in the chain, discarding CA 29 | # certificates 30 | [ "$depth" -eq 0 ] && { 31 | 32 | end_date=$(openssl x509 -noout -in "$peer_cert" -enddate | cut -d "=" -f 2) 33 | common_name=$(echo "$subject" | grep -o 'CN=[a-z_0-9]*' | cut -d "=" -f 2) 34 | email=$(echo "$subject" | grep -o 'emailAddress=.*,' | cut -d "=" -f 2 | head -c -2) 35 | serial=$(openssl x509 -noout -in "$peer_cert" -serial | cut -d "=" -f 2) 36 | 37 | # Prune old records with the same common name 38 | grep -v "$common_name" $END_DATE_FILE > tmp 39 | mv -f tmp $END_DATE_FILE 40 | 41 | echo "$common_name,$serial,$email,$end_date" >> $END_DATE_FILE 42 | } 43 | 44 | rm $LOCKFILE 45 | 46 | exit 0 -------------------------------------------------------------------------------- /to_copy/usr/bin/openvpn-client-disconnect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CN_FILE="/tmp/common_names.txt" 4 | touch $CN_FILE 5 | 6 | # Stores the common_name from the client who has just disconnected 7 | # This value will be used by private-end-point-docker-stats 8 | # in order to avoid sending stats for this client 9 | echo $common_name >> $CN_FILE 10 | 11 | encryptme-stats --metric vpn_session --server $ENCRYPTME_STATS_SERVER $ENCRYPTME_STATS_ARGS 12 | 13 | # Prune common_name from file or erase the file 14 | grep -v $common_name $CN_FILE > tmp && \ 15 | mv -f tmp $CN_FILE || \ 16 | rm -f $CN_FILE -------------------------------------------------------------------------------- /to_copy/usr/bin/openvpn-on-connect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | 4 | # This script is run by openvpn server when a new client conection 5 | # has been established, defined by "client-connect" 6 | 7 | 8 | LOCKFILE="/etc/encryptme/data/.cert_lock" 9 | SESSION_MAP="/etc/encryptme/data/cert_session_map" 10 | END_DATE_FILE="/etc/encryptme/data/cert_end_date" 11 | 12 | 13 | fail() { 14 | echo "${1-command failed}" >&2 15 | [ -f "$LOCKFILE" ] && rm $LOCKFILE 16 | exit ${2:-1} 17 | } 18 | 19 | 20 | count=0 21 | while [ -f "$LOCKFILE" ]; do 22 | sleep 1 23 | ((count++)) 24 | ## If this took five seconds.. things are wrong 25 | ## start over.. maybe we lost something 26 | [ $count -gt 5 ] && fail "Could not get a lock" 27 | done 28 | echo "$$" > $LOCKFILE 29 | 30 | # Prune old records with the same common name 31 | grep -v "$common_name" $SESSION_MAP > tmp 32 | mv -f tmp $SESSION_MAP 33 | 34 | serial_0=$(echo "$tls_serial_hex_0" |tr -d : | tr '[:lower:]' '[:upper:]') 35 | serial_1=$(echo "$tls_serial_hex_1" |tr -d : | tr '[:lower:]' '[:upper:]') 36 | serial_2=$(echo "$tls_serial_hex_2" |tr -d : | tr '[:lower:]' '[:upper:]') 37 | 38 | email=$(grep "$serial_0" $END_DATE_FILE | tail -1 | cut -d "," -f 3) 39 | end_date=$(grep "$serial_0" $END_DATE_FILE | tail -1 | cut -d "," -f 4) 40 | 41 | # Stores client certificate info 42 | echo "$common_name,$serial_0,$serial_1,$serial_2,$email,$end_date" >> $SESSION_MAP 43 | 44 | grep -v "$serial_0" $END_DATE_FILE > tmp 45 | mv -f tmp $END_DATE_FILE 46 | 47 | 48 | rm $LOCKFILE 49 | -------------------------------------------------------------------------------- /to_copy/usr/bin/pep-filter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | BASE_DIR=$(cd $(dirname "$0") && pwd -P) 4 | SCRIPT_NAME=$(basename "$0") 5 | 6 | FILTERS_DIR="/etc/encryptme/filters" 7 | DOMAIN_RE="^([A-Za-z0-9-]+\.)+[A-Za-z]{2,}$" 8 | CIDR_RE="^([0-9]{1,3}\.){3}[0-9]{1,3}(\/[0-9]{1,3})?$" 9 | TMP_DIR="/tmp/$SCRIPT_NAME.$$" && mkdir -p "$TMP_DIR" \ 10 | || fail "Failed to create temporary directory '$TMP_DIR'" 11 | 12 | 13 | usage() { 14 | cat << EOF 15 | usage: $SCRIPT_NAME ACTION ARGS 16 | 17 | Automated DNS and IPv4 CIDR filtering based on arbitrary lists. Reads STDIN 18 | for a list of domains or IPv4 CIDR ranges. 19 | 20 | - This script must be used on container startup (e.g. server rebooted) 21 | to dynamically restore filtering rules. 22 | 23 | ACTIONS: 24 | 25 | append NAME Add domains/ips to a block list 26 | replace NAME Replace all domains/ips in a block list with new ones 27 | prune NAME Delete domains/ips from a block list 28 | reset Remove all domain and IP filtering 29 | reload Reload all domain and IP filtering 30 | 31 | EXAMPLES: 32 | 33 | # Add the list 'security' 34 | $SCRIPT_NAME append security < /opt/lists/security.txt 35 | echo 'google.com' | ./$SCRIPT_NAME append security 36 | 37 | # Stop filtering all domains/IPs in 'security' list 38 | $SCRIPT_NAME prune security 39 | 40 | # Reset everything to stop all filtering 41 | $SCRIPT_NAME reset 42 | EOF 43 | 44 | } 45 | 46 | 47 | cleanup() { 48 | [ -d "$TMP_DIR" ] && rm -rf "$TMP_DIR" &>/dev/null 49 | } 50 | 51 | 52 | fail() { 53 | echo "! $1" >&2 54 | cleanup 55 | exit 1 56 | } 57 | 58 | 59 | reload_domains() { 60 | local cmd="/opt/dns-filter/server.py" 61 | "$cmd" stop 62 | "$cmd" start 63 | } 64 | 65 | 66 | reload_ips() { 67 | local split_dir="$TMP_DIR/split" 68 | local cmds list_name OLDIFS 69 | 70 | mkdir -p "$split_dir" \ 71 | || fail "Failed to create temporary directory '$split_dir'" 72 | 73 | reset_ips 74 | 75 | ls "$FILTERS_DIR" | grep "\.ips\.blacklist$" | while read list; do 76 | list_name="$(echo $list | cut -d'.' -f1)" 77 | 78 | cat "$FILTERS_DIR/$list" \ 79 | | split -d -l 65000 - "$split_dir/$list_name." 80 | 81 | ls "$split_dir" | grep -E "$list_name\.[0-9]{2}" | while read sublist; do 82 | cmds=() 83 | cmds+=("create $sublist hash:net family inet hashsize 1024 maxelem 65536") 84 | 85 | while read cidr; do 86 | cmds+=("add $sublist $cidr") 87 | done < "$split_dir/$sublist" 88 | 89 | OLDIFS="$IFS"; IFS=$'\n' 90 | echo "${cmds[*]}" | ipset restore 91 | IFS="$OLDIFS" 92 | 93 | /sbin/iptables-save | grep -Eq -- "--match-set \<$sublist\>" || { 94 | /usr/sbin/iptables -I ENCRYPTME 2 -m set --match-set "$sublist" dst -j DROP \ 95 | || fail "Failed to insert iptables rule $sublist" 96 | } 97 | done 98 | 99 | done 100 | } 101 | 102 | 103 | add_ips() { 104 | local list_name="$1" 105 | local new_ip_file="$2" 106 | local tmp_old_ip_file="$TMP_DIR/$list_name.cidr.old" 107 | local split_dir="$TMP_DIR/split" 108 | local ip_file="$FILTERS_DIR/$list_name.ips.blacklist" 109 | local cmds OLDIFS 110 | 111 | touch "$tmp_old_ip_file" || fail "Failed to create temp old ip file" 112 | 113 | mkdir -p "$split_dir" \ 114 | || fail "Failed to create temporary directory '$split_dir'" 115 | 116 | mkdir -p "$FILTERS_DIR" || fail "Failed to create blacklists directory" 117 | 118 | /sbin/ipset -n list | grep -E "$list_name\.[0-9]{2}" | while read sublist; do 119 | ipset list "$sublist" \ 120 | | grep -Eo "$CIDR_RE" >> "$tmp_old_ip_file" \ 121 | || fail "Failed to get IP list for '$sublist'" 122 | /sbin/iptables-save | grep -Eq -- "--match-set \<$sublist\>" && { 123 | /sbin/iptables -D ENCRYPTME -m set --match-set "$sublist" dst -j DROP \ 124 | || fail "Failed to delete iptables rule for the list $sublist" 125 | } 126 | /sbin/ipset destroy "$sublist" \ 127 | || fail "Failed to delete ipset $sublist" 128 | done 129 | 130 | # Save IPs to file to be used when container restarts (e.g. reboot) 131 | cat "$tmp_old_ip_file" "$new_ip_file" > "$ip_file" 132 | 133 | cat "$ip_file" \ 134 | | sort -u \ 135 | | split -d -l 65000 - "$split_dir/$list_name." 136 | 137 | ls "$split_dir" | grep -E "$list_name\.[0-9]{2}" | while read list; do 138 | cmds=() 139 | cmds+=("create $list hash:net family inet hashsize 1024 maxelem 65536") 140 | 141 | while read cidr; do 142 | cmds+=("add $list $cidr") 143 | done < "$split_dir/$list" 144 | 145 | OLDIFS="$IFS"; IFS=$'\n' 146 | echo "${cmds[*]}" | ipset restore 147 | IFS="$OLDIFS" 148 | 149 | /sbin/iptables-save | grep -Eq -- "--match-set \<$list\>" || { 150 | /usr/sbin/iptables -I ENCRYPTME 2 -m set --match-set "$list" dst -j DROP \ 151 | || fail "Failed to insert iptables rule $list" 152 | } 153 | done 154 | } 155 | 156 | 157 | add_domains() { 158 | local list_name="$1" 159 | local new_domain_file="$2" 160 | local tmp_domain_file="$TMP_DIR/domains.old" 161 | local domain_file="$FILTERS_DIR/$list_name.domains.blacklist" 162 | 163 | touch "$tmp_domain_file" || fail "Failed to create temp domain file" 164 | mkdir -p "$FILTERS_DIR" || fail "Failed to create blacklists directory" 165 | 166 | # keep things clean add keep dupes scrubbed out as we update the domain list 167 | [ -s "$domain_file" ] && \ 168 | cat "$domain_file" | sort -u > "$tmp_domain_file" 169 | 170 | cat "$tmp_domain_file" "$new_domain_file" | sort -u > "$domain_file" \ 171 | || fail "Failed to write $domain_file" 172 | 173 | reload_domains \ 174 | || fail "Failed to reload dns-filter" 175 | } 176 | 177 | 178 | prune_list() { 179 | local list_name="$1" 180 | local domain_file="$FILTERS_DIR/$list_name.domains.blacklist" 181 | local ip_file="$FILTERS_DIR/$list_name.ips.blacklist" 182 | 183 | # delete the IP table rule and ipset list 184 | /sbin/ipset -n list | grep -E "$list_name\.[0-9]{2}" | while read sublist; do 185 | /sbin/iptables-save | grep -Eq -- "--match-set \<$sublist\>" && { 186 | /sbin/iptables -D ENCRYPTME -m set --match-set "$sublist" dst -j DROP \ 187 | || fail "Failed to delete iptables rule for the list $sublist" 188 | } 189 | /sbin/ipset destroy "$sublist" \ 190 | || fail "Failed to delete ipset $sublist" 191 | done 192 | 193 | # Delete a Domain blacklist file 194 | [ -f "$domain_file" ] && { 195 | rm -f "$domain_file" 196 | # reload_filter 197 | reload_domains 198 | } 199 | 200 | # Delete an IP blacklist file 201 | [ -f "$ip_file" ] && rm -f "$ip_file" 202 | 203 | return 0 204 | } 205 | 206 | 207 | reset_ips() { 208 | # delete all ipset lists and iptables rules 209 | /sbin/ipset -n list | while read list_name; do 210 | /sbin/iptables-save | grep -Eq -- "--match-set \<$list_name\>" && { 211 | /sbin/iptables -D ENCRYPTME -m set --match-set "$list_name" dst -j DROP \ 212 | || fail "Failed to delete iptables rule for the list $list_name" 213 | } 214 | /sbin/ipset destroy "$list_name" \ 215 | || fail "Failed to delete ipset $list_name" 216 | done 217 | } 218 | 219 | 220 | reset_filters() { 221 | # remove our blacklists 222 | rm -rf "$FILTERS_DIR" || fail "Failed to delete blacklists" 223 | reset_ips 224 | reload_domains 225 | } 226 | 227 | 228 | # reads stdin to parse IPv4 CIDR ranges and domain names and filter them out 229 | append_list() { 230 | local list_name="$1" 231 | local cidr_file="$TMP_DIR/$list_name.cidr" 232 | local domain_file="$TMP_DIR/$list_name.domains" 233 | local stdin="$TMP_DIR/$list_name.stdin" 234 | 235 | cat > "$stdin" 236 | cat "$stdin" | grep -E "$CIDR_RE" > "$cidr_file" 237 | cat "$stdin" | grep -E "$DOMAIN_RE" > "$domain_file" 238 | 239 | [ -s "$cidr_file" ] && add_ips "$list_name" "$cidr_file" 240 | [ -s "$domain_file" ] && add_domains "$list_name" "$domain_file" 241 | } 242 | 243 | 244 | [ $# -ge 1 ] || { 245 | usage 246 | fail "No action given" 247 | } 248 | 249 | case "$1" in 250 | append|replace|prune|reset|reload) 251 | action="$1" 252 | shift 253 | ;; 254 | *) 255 | usage 256 | fail "Invalid action: '$1'" 257 | esac 258 | 259 | 260 | [ "$action" = "append" ] && { 261 | [ $# -eq 1 ] || fail "No list name given to append to" 262 | list_name="$1" && shift 263 | append_list "$list_name" 264 | } 265 | 266 | [ "$action" = "replace" ] && { 267 | [ $# -eq 1 ] || fail "No list name given to replace" 268 | list_name="$1" && shift 269 | prune_list "$list_name" 270 | append_list "$list_name" 271 | } 272 | 273 | [ "$action" = "prune" ] && { 274 | [ $# -eq 1 ] || fail "No list name given to prune from" 275 | list_name="$1" && shift 276 | prune_list "$list_name" 277 | } 278 | 279 | [ "$action" = "reset" ] && { 280 | reset_filters 281 | } 282 | 283 | [ "$action" = "reload" ] && { 284 | reload_ips 285 | reload_domains 286 | } 287 | 288 | cleanup 289 | 290 | exit 0 291 | -------------------------------------------------------------------------------- /to_copy/usr/bin/refresh-crls.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 4 | export PATH 5 | 6 | cd /etc/encryptme/pki 7 | 8 | sleep $(($RANDOM % 300)) 9 | cloak-server --config /etc/encryptme/encryptme.conf --quiet crls \ 10 | --infile crl_urls.txt \ 11 | --out crls \ 12 | --post-hook "cat crls/*.pem > new-crls.pem; mv new-crls.pem crls.pem; /usr/sbin/ipsec rereadcrls; /usr/sbin/ipsec purgecrls" 13 | -------------------------------------------------------------------------------- /to_copy/usr/bin/refresh-wireguard.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import namedtuple 4 | from subprocess import Popen, PIPE 5 | import json 6 | import os 7 | import re 8 | import sys 9 | 10 | import pidfile 11 | 12 | 13 | WGPeer = namedtuple('WGPeer', [ 14 | 'public_key', 'preshared_key', 'endpoint', 'allowed_ips', 15 | 'lastest_handshake', 'bytes_up', 'bytes_down', 'keep_alives' 16 | ]) 17 | 18 | EME_DIR = os.environ.get('EME_DIR', '/etc/encryptme/wireguard') 19 | PEERS_FILE = EME_DIR + '/peers.json' 20 | 21 | 22 | def rem(msg, color='33'): 23 | sys.stdout.write(f'\033[1;{color}m#\033[0;{color}m {msg}\033[0m\n') 24 | 25 | 26 | def run(cmd, dryrun=False, verbose=False): 27 | """ 28 | Run a shell command, raising a RunTime exception if it failed. Returns 29 | (stdout, stderr). If dryrun=True prints the command and returns (None, 30 | None) instead. Always prints the command if verbose=True. 31 | """ 32 | if dryrun or verbose: 33 | rem('%s' % (' '.join(cmd)), '32') 34 | if dryrun: 35 | return None, None 36 | proc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 37 | stdout, stderr = proc.communicate() 38 | if proc.returncode != 0: 39 | raise RuntimeError("Failed to run %s: %s" % ( 40 | cmd[0], 41 | stderr 42 | )) 43 | return stdout.decode('utf-8'), stderr.decode('utf-8') 44 | 45 | 46 | def fetch_eme_conf(base_url, config_file=None, verbose=False): 47 | """ 48 | Downloads Encrypt.me peer configuration information for valid users. 49 | Returns a tuple of response data and a parse mapping of public key to 50 | private IPv4 address. 51 | """ 52 | # e.g.: 53 | # { 54 | # "wireguard_peers": { 55 | # "usr_d7eaondzddogjitf": { 56 | # "dev_ncfmdpdnyr4kbvrg": { 57 | # "public_key": "ljXdE1MLQ3KRPtmqs38lI9E8Zo5iSoIN7BfYg7c28AE=", 58 | # "private_ipv4_address": "100.64.128.1/32" 59 | # "private_ipv4_address_int": 1684045825 60 | # } 61 | # } 62 | # } 63 | # } 64 | cmd = [ 65 | 'cloak-server', 66 | '--base_url', base_url, 67 | ] 68 | if config_file: 69 | cmd.append('--config') 70 | cmd.append(config_file) 71 | cmd.append('wireguard') 72 | try: 73 | stdout, stderr = run(cmd, verbose=verbose) 74 | except RuntimeError: 75 | stdout, eme_conf = '', {} 76 | else: 77 | eme_conf = {} # maps pubkey to private IP 78 | try: 79 | eme_data = json.loads(stdout) 80 | except (TypeError, ValueError): 81 | eme_data = {} 82 | for user in eme_data.get('wireguard_peers'): 83 | user_data = eme_data['wireguard_peers'][user] 84 | for device in user_data: 85 | device_data = user_data[device] 86 | eme_conf[device_data['public_key']] = device_data['private_ipv4_address'] 87 | return stdout, eme_conf 88 | 89 | 90 | def fetch_wg_conf(wg_iface, verbose=False): 91 | """ 92 | Parses WireGuard dump info into named tuples. 93 | """ 94 | stdout, stderr = run(['wg', 'show', wg_iface, 'dump'], verbose=verbose) 95 | wg_conf = {} # maps pubkey to WGPeer namedtuples 96 | wg_re = re.compile('\s+') 97 | # first line is server interface info, so we skip it 98 | for line in stdout.strip().split('\n')[1:]: 99 | clean_line = line.strip() 100 | if not clean_line: 101 | continue 102 | wg_peer = WGPeer(*wg_re.split(line)) 103 | wg_conf[wg_peer.public_key] = wg_peer 104 | return wg_conf 105 | 106 | 107 | def wg_up(wg_iface, pubkey, allowed_ips, dryrun=False, verbose=False): 108 | """ 109 | Add a peer to WireGuard. 110 | """ 111 | run(['ip', '-4', 'route', 'add', allowed_ips, 'dev', wg_iface], dryrun, verbose) 112 | run(['wg', 'set', wg_iface, 'peer', pubkey, 'allowed-ips', allowed_ips], dryrun, verbose) 113 | 114 | 115 | def wg_down(wg_iface, wg_peer, dryrun=False, verbose=False): 116 | """ 117 | Remove a peer from a WireGuard interface. 118 | """ 119 | run(['wg', 'set', wg_iface, 'peer', wg_peer.public_key, 'remove'], dryrun, verbose) 120 | run(['ip', '-4', 'route', 'del', wg_peer.allowed_ips, 'dev', wg_iface], dryrun, verbose) 121 | 122 | 123 | def main(wg_iface, base_url=None, config_file=None, verbose=False, dryrun=False): 124 | """ 125 | Fetches data from Encrypt.me, parses local WireGuard interface 126 | configuration information and ensures all peers are configured correctly 127 | based on any changes. 128 | """ 129 | # get the config data from Encrypt.me and from what is on the server now 130 | # then, using wg interface data we can decide: 131 | with pidfile.PIDFile('/tmp/refresh-wireguard.pid'): 132 | if dryrun: 133 | rem("*** DRY RUN (no changes will be made) ***") 134 | eme_resp_data, eme_conf = fetch_eme_conf(base_url, config_file, verbose=verbose) 135 | if verbose: 136 | rem("Found %d peers from Encrypt.me; saving to %s" % ( 137 | len(eme_conf), PEERS_FILE 138 | )) 139 | if not dryrun: 140 | with open(PEERS_FILE, 'w') as peers_file: 141 | peers_file.write(eme_resp_data) 142 | wg_conf = fetch_wg_conf(wg_iface, verbose=verbose) 143 | if verbose: 144 | rem("Found %d local WireGuard peers" % (len(wg_conf))) 145 | eme_pubkeys = frozenset(eme_conf.keys()) 146 | wg_pubkeys = frozenset(wg_conf.keys()) 147 | 148 | # --- we need to determine: --- 149 | # * which peers to remove 150 | pubkeys_old = wg_pubkeys - eme_pubkeys 151 | if verbose: 152 | rem("Removing %d old peers" % len(pubkeys_old)) 153 | for pubkey in pubkeys_old: 154 | wg_down(wg_iface, wg_conf[pubkey], dryrun) 155 | 156 | # * which peers to possibly change the IP address of 157 | pubkeys_same = wg_pubkeys & eme_pubkeys 158 | changed = 0 159 | for pubkey in pubkeys_same: 160 | eme_ipv4 = eme_conf[pubkey] 161 | wg_ipv4 = wg_conf[pubkey].allowed_ips 162 | if eme_ipv4 != wg_ipv4: 163 | changed += 1 164 | wg_down(wg_iface, wg_conf[pubkey], dryrun, verbose) 165 | wg_up(wg_iface, pubkey, eme_conf[pubkey], dryrun, verbose) 166 | if verbose: 167 | rem("Changed %d peers to new IP addresses" % (changed)) 168 | 169 | # * which peers to add 170 | pubkeys_new = eme_pubkeys - wg_pubkeys 171 | if verbose: 172 | rem("Adding %d new peers" % len(pubkeys_new)) 173 | for pubkey in pubkeys_new: 174 | wg_up(wg_iface, pubkey, eme_conf[pubkey], dryrun, verbose) 175 | 176 | 177 | # 178 | # Parse out "foo=bar" type parameters and runs the script. 179 | # 180 | if __name__ == '__main__': 181 | # sanity checks 182 | if not os.path.isdir(EME_DIR): 183 | raise Exception("Failed to find Encrypt.me WireGuard directory") 184 | 185 | # poor man's argparse 186 | args = { 187 | 'wg_iface': 'wg0', 188 | 'config_file': None, 189 | 'base_url': os.environ['ENCRYPTME_API_URL'], 190 | 'dryrun': False, 191 | 'verbose': False, 192 | } 193 | for arg in sys.argv[1:]: 194 | if '=' not in arg: 195 | raise Exception("Expected: arg=value; invalid parameter: %s" % (arg)) 196 | (arg_name, arg_val) = arg.split('=', 2) 197 | if arg_name not in args: 198 | raise Exception("Invalid arg: %s" % arg_name) 199 | if arg_val.isdigit(): 200 | arg_val = int(arg_val) 201 | args[arg_name] = arg_val 202 | try: 203 | main(**args) 204 | except pidfile.AlreadyRunningError: 205 | sys.stdout.write("%s already running." % os.path.basename(__file__)) 206 | -------------------------------------------------------------------------------- /to_copy/usr/bin/reload-certificate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | le_dir="/etc/letsencrypt/live" 4 | ipsec_dir="/etc/strongswan/ipsec.d/" 5 | 6 | # copy our public CA-issued cert info to the expected spot 7 | # ----------------------------------------------------------------------------- 8 | echo "Copying LetsEncrypt certificates to '$ipsec_dir'" >&1 9 | for domain_dir in "$le_dir"/*; do 10 | [ -f "$domain_dir/privkey.pem" ] || continue 11 | cp "$domain_dir/privkey.pem" "$ipsec_dir/private/letsencrypt.pem" 12 | cp "$domain_dir/fullchain.pem" "$ipsec_dir/certs/letsencrypt.pem" 13 | cp "$domain_dir/chain.pem" "$ipsec_dir/cacerts/letsencrypt.pem" 14 | break # we should only ever be handling one domain at a time... 15 | done 16 | 17 | 18 | # and restart the VPN daemons 19 | # ----------------------------------------------------------------------------- 20 | echo "Reloading Strongswan" >&2 21 | /usr/sbin/ipsec restart 22 | 23 | # terminate openvpn process(es) 24 | echo "Reloading OpenVPN" >&2 25 | ps xww -o pid,cmd \ 26 | | grep '/usr/sbin/openvpn' \ 27 | | grep -v grep \ 28 | | while read pid cmd; do 29 | # kill it off 30 | kill "$pid" 31 | while true; do 32 | ps -p "$pid" &>/dev/null || break 33 | done 34 | done 35 | 36 | # start up all the necessary OpenVPN servers (just 1, we expect) 37 | for ((i=0; ;i++)); do 38 | # and start it if a config file exists for this number 39 | conf="/etc/openvpn/server-$i.conf" 40 | [ -f "$conf" ] || break 41 | echo "Starting OpenVPN from '$conf'" >&2 42 | nohup /usr/sbin/openvpn \ 43 | --status /var/run/openvpn/server-$i.status 10 \ 44 | --cd /etc/openvpn \ 45 | --script-security 2 \ 46 | --config "$conf" \ 47 | --writepid /var/run/openvpn/server-$i.pid \ 48 | --management /var/run/openvpn/server-$i.sock unix \ 49 | --verb 0 \ 50 | &>/dev/null /dev/null 2>&1 12 | retval=$? 13 | /sbin/iptables -D INPUT -p tcp --dport http -j ACCEPT 14 | 15 | exit $retval 16 | -------------------------------------------------------------------------------- /to_copy/usr/bin/template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """Substitute variables using jinja2.""" 4 | 5 | import json 6 | import jinja2 7 | import argparse 8 | import socket 9 | 10 | 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument('-s', '--source', help='source template file', 13 | required=True, type=str) 14 | parser.add_argument('-x', '--extra', help='extra source template files; added to doc based on filename', 15 | type=str, action='append', default=[]) 16 | parser.add_argument('-o', '--out', help='output file', 17 | required=True, type=str) 18 | parser.add_argument('-d', '--data', help='json data file', 19 | required=True, type=str) 20 | parser.add_argument('-v', '--var', type=str, action='append', 21 | default=[], help="set variable to value (--var foo=baz)") 22 | args = parser.parse_args() 23 | 24 | # https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib 25 | ip = [l for l in ([ip for ip in 26 | socket.gethostbyname_ex(socket.gethostname())[2] 27 | if not ip.startswith("127.")][:1], 28 | [[(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) 29 | for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) 30 | if l][0][0] 31 | 32 | 33 | def apply_template(data, source_file): 34 | """Run file through Jinja2 Template system.""" 35 | template = jinja2.Template(source_file.read()) 36 | return template.render(**data) 37 | 38 | 39 | # get our source data to start 40 | data = None 41 | with open(args.data) as data_file: 42 | data = json.load(data_file) 43 | data = dict(data=data, ip=ip) 44 | 45 | # extend with any extra data 46 | for extra in args.extra: 47 | with open(extra) as data_file: 48 | extra_name = extra.split('.')[0].split('/')[-1] 49 | data[extra_name] = json.load(data_file) 50 | 51 | # plus any one-off vars from the command line 52 | if args.var: 53 | for var in args.var: 54 | splitted = var.split("=", 1) 55 | if len(splitted) == 1: 56 | data[splitted[0]] = '' 57 | else: 58 | data[splitted[0]] = splitted[1] 59 | 60 | # finally, apply this all to the source template 61 | with open(args.source) as source_file: 62 | content = apply_template(data, source_file) 63 | with open(args.out, "w") as dest_file: 64 | dest_file.write(content) 65 | -------------------------------------------------------------------------------- /to_copy/usr/bin/update-pki.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 4 | export PATH 5 | 6 | cloak() { 7 | cloak-server --config /etc/encryptme/encryptme.conf "$@" 8 | } 9 | 10 | 11 | # in case the server issued a new certificate for some reason 12 | cloak --quiet pki \ 13 | --out /etc/encryptme/pki \ 14 | --post-hook /usr/bin/reload-certificate.sh 15 | 16 | # additionally, we need to proactively ensure we're renewing what we have 17 | openssl x509 -noout -in /etc/encryptme/pki/server.pem -checkend $((86400*30)) || { 18 | cloak req --key /etc/encryptme/pki/cloak.pem \ 19 | && /usr/bin/reload-certificate.sh 20 | } 21 | -------------------------------------------------------------------------------- /to_copy/usr/local/unbound-1.7/etc/unbound/unbound.conf: -------------------------------------------------------------------------------- 1 | server: 2 | verbosity: 1 3 | interface: 100.64.0.1 4 | access-control: 10.0.0.0/8 allow 5 | access-control: 100.64.0.0/10 allow 6 | access-control: 127.0.0.1 allow 7 | do-daemonize: no 8 | do-ip6: no 9 | use-syslog: no 10 | chroot: "" 11 | directory: "" 12 | username: "" 13 | logfile: "" 14 | module-config: "validator python iterator" 15 | 16 | python: 17 | python-script: "/usr/local/unbound-1.7/sbin/filter_client.py" 18 | -------------------------------------------------------------------------------- /to_copy/usr/local/unbound-1.7/sbin/filter_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Unbound client script - Talks to a daemon powered by "/opt/dns-filter/server.py" 3 | to do DNS filtering. 4 | 5 | Requires Python 2.7 and uses a version of python included in Unbound: 6 | 7 | /usr/local/unbound-1.7/etc/unbound/usr/lib64/python2.7/site-packages 8 | """ 9 | 10 | import socket 11 | import json 12 | import os 13 | from time import sleep 14 | 15 | intercept_address = "0.0.0.0" 16 | sock_file = "/usr/local/unbound-1.7/etc/unbound/dns_filter.sock" 17 | sock_exist = False 18 | doh_canary_domains = ["use-application-dns.net"] 19 | doh_provider_domains = [ 20 | "adblock.mydns.network", "cloudflare-dns.com", "commons.host", 21 | "dns-family.adguard.com", "dns-nyc.aaflalo.me", "dns.aa.net.uk", 22 | "dns.aaflalo.me", "dns.adguard.com", "dns.alidns.com", 23 | "dns.containerpi.com", "dns.digitale-gesellschaft.ch", 24 | "dns.dns-over-https.com", "dns.dnshome.de", "dns.dnsoverhttps.net", 25 | "dns.flatuslifir.is", "dns.google", "dns.hostux.net", "dns.quad9.net", 26 | "dns.rubyfish.cn", "dns.switch.ch", "dns.twnic.tw", "dns10.quad9.net", 27 | "dns11.quad9.net", "dns9.quad9.net", "dnsforge.de", "doh-2.seby.io", 28 | "doh-de.blahdns.com", "doh-fi.blahdns.com", "doh-jp.blahdns.com", 29 | "doh.42l.fr", "doh.applied-privacy.net", "doh.armadillodns.net", 30 | "doh.captnemo.in", "doh.centraleu.pi-dns.com", "doh.cleanbrowsing.org", 31 | "doh.crypto.sx", "doh.dns.sb", "doh.dnslify.com", "doh.eastas.pi-dns.com", 32 | "doh.eastau.pi-dns.com", "doh.eastus.pi-dns.com", 33 | "doh.familyshield.opendns.com", "doh.ffmuc.net", "doh.li", 34 | "doh.libredns.gr", "doh.northeu.pi-dns.com", "doh.opendns.com", 35 | "doh.pi-dns.com", "doh.powerdns.org", "doh.seby.io:8443", "doh.tiar.app", 36 | "doh.tiarap.org", "doh.westus.pi-dns.com", "doh.xfinity.com", 37 | "dohdot.coxlab.net", "example.doh.blockerdns.com", 38 | "family.canadianshield.cira.ca", "family.cloudflare-dns.com", 39 | "fi.doh.dns.snopyta.org", "ibksturm.synology.me", "ibuki.cgnat.net", 40 | "jcdns.fun", "jp.tiar.app", "jp.tiarap.org", "mozilla.cloudflare-dns.com", 41 | "odvr.nic.cz", "ordns.he.net", "private.canadianshield.cira.ca", 42 | "protected.canadianshield.cira.ca", "rdns.faelix.net", 43 | "resolver-eu.lelux.fi", "security.cloudflare-dns.com", 44 | ] 45 | 46 | 47 | def _check_for_socket(): 48 | global sock_exist 49 | for _ in range(4): 50 | if os.path.exists(sock_file): 51 | sock_exist = True 52 | break 53 | sleep(0.25) 54 | 55 | 56 | def _is_blocked(name): 57 | while True: 58 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 59 | sock.connect(sock_file) 60 | sock.sendall(json.dumps({'domain': name})) 61 | resp = str(sock.recv(2048)) 62 | return json.loads(resp) 63 | 64 | 65 | def init(id, cfg): 66 | _check_for_socket() 67 | return True 68 | 69 | 70 | def deinit(id): 71 | return True 72 | 73 | 74 | def inform_super(id, qstate, superqstate, qdata): 75 | return True 76 | 77 | 78 | def operate(id, event, qstate, qdata): 79 | if (event == MODULE_EVENT_NEW) or (event == MODULE_EVENT_PASS): 80 | 81 | # server isn't running? do nothing 82 | if not sock_exist: 83 | qstate.ext_state[id] = MODULE_WAIT_MODULE 84 | return True 85 | 86 | name = qstate.qinfo.qname_str.rstrip('.') 87 | is_blocked, disable_doh = _is_blocked(name) 88 | 89 | if disable_doh: 90 | if name in doh_canary_domains: 91 | qstate.return_rcode = RCODE_NXDOMAIN 92 | qstate.ext_state[id] = MODULE_FINISHED 93 | return True 94 | 95 | if name in doh_provider_domains and not is_blocked: 96 | qstate.return_rcode = RCODE_NOERROR 97 | qstate.ext_state[id] = MODULE_FINISHED 98 | return True 99 | 100 | # not blocked? do nothing 101 | if not is_blocked: 102 | qstate.ext_state[id] = MODULE_WAIT_MODULE 103 | return True 104 | 105 | # otherwise, respond with our intercept address 106 | msg = DNSMessage(qstate.qinfo.qname_str, RR_TYPE_A, 107 | RR_CLASS_IN, PKT_QR | PKT_RA | PKT_AA) 108 | if (qstate.qinfo.qtype == RR_TYPE_A) or ( 109 | qstate.qinfo.qtype == RR_TYPE_ANY): 110 | msg.answer.append( 111 | "%s 10 IN A %s" % (qstate.qinfo.qname_str, 112 | intercept_address)) 113 | 114 | if not msg.set_return_msg(qstate): 115 | qstate.ext_state[id] = MODULE_ERROR 116 | return True 117 | 118 | qstate.return_msg.rep.security = 2 119 | 120 | qstate.return_rcode = RCODE_NOERROR 121 | qstate.ext_state[id] = MODULE_FINISHED 122 | return True 123 | 124 | elif event == MODULE_EVENT_MODDONE: 125 | qstate.ext_state[id] = MODULE_FINISHED 126 | return True 127 | 128 | qstate.ext_state[id] = MODULE_ERROR 129 | return True 130 | -------------------------------------------------------------------------------- /to_extract/unbound-1.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/encryptme/private-end-points-docker/908dce50356faadd9b4f6c9435b4ccf69e336650/to_extract/unbound-1.7.tar.gz --------------------------------------------------------------------------------