This is a step-by-step guide to configuring and managing a domain, remote server and hosted services, such as VPN, a private and obfuscated Tor bridge, and encrypted chat, using the Debian GNU/Linux operating system and other free software.
This guide is written for Google Compute Engine (GCE), but will very likely work well on other service providers, such as Linode or Amazon AWS, or any computer which will run GNU/Linux, such as a PC Engines APU in a closet. It uses recommended configuration files from drduh/config.
If you are not sure what a domain name is, see the Wikipedia article and decide if you would like to create one at all.
I had decided to purchase duh.to from Tonic, a .to top level domain registrar. A 5 year registration cost $200 - a steep price, but not unreasonable for an esoteric ccTLD with many available short, memorable, three-letter domain names. Tonic.to also does not maintain a public whois database, which is a privacy advantage.
You could instead purchase a less expensive .com, .net or any available domain name from a variety of TLDs and registrars, though be aware of not all offer domain privacy, for instance the .us ccTLD.
After purchasing your domain, configure DNS settings. To use Google Cloud DNS with Tonic:
Wait for DNS records to propagate, which may take several hours. While you wait, feel free to learn more about Tonga.
Eventually, a WHOIS lookup will return the NS record of your hosting provider:
$ whois duh.to
Tonic whoisd V1.1
duh ns-cloud-c1.googledomains.com
duh ns-cloud-c2.googledomains.comIf it doesn't look right, log in to Tonic or your registrar and update DNS information accordingly.
Download and configure the gcloud command line tool.
Log in and enter the verification code:
$ gcloud auth login
$ gcloud set project debian-privsec-cloudSet the PROJECT, INSTANCE, NETWORK, TYPE, and ZONE variables, as well as a recent image version:
$ PROJECT=debian-privsec-cloud
$ INSTANCE=debian-privsec-standard
$ NETWORK=debian-privsec-net
$ TYPE=n1-standard-1
$ ZONE=us-east1-a
$ IMAGE=$(gcloud compute images list | grep debian | awk '{print $1}')Create a dedicated network:
$ gcloud compute networks create $NETWORKCreate an instance:
$ gcloud compute --project=$PROJECT instances create $INSTANCE --zone=$ZONE --subnet=$NETWORK \
--machine-type=$TYPE --network-tier=PREMIUM --can-ip-forward --no-restart-on-failure --maintenance-policy=MIGRATE \
--no-service-account --no-scopes --image=$IMAGE --image-project=debian-cloud \
--boot-disk-size=40GB --boot-disk-type=pd-standard --boot-disk-device-name=$INSTANCEAdd a firewall rule for remote access:
$ gcloud compute firewall-rules create ssh-tcp-22 --network $NETWORK --allow tcp:22 --source-ranges $(curl -s https://icanhazip.com)First, create a network to define the firewall policy later.
Navigate to VM instances and select Create Instance.
Pick a name, zone and machine type. A "standard" single-vCPU or even shared "micro" or "small" machine with Debian 9 are fine defaults:
Select the Networking tab and select your pre-configured network, if any. Apply any desired network tags while here, too.
Select Create to start the instance.
This step is optional.
Once you have an External IP assigned, you may want to configure a DNS record. To do so, go to Networking > Cloud DNS and select Create Zone to create a new DNS zone.
Create an A record for the domain by selecting Add Record Set:
Select Create.
After a short while, verify an A record is returned with the correct IPv4 address for the instance:
$ dig +short a duh.to
104.197.215.107If it doesn't work, wait longer for records to propagate, or try specifying the registrar's name severs:
$ dig +short a duh.to @ns-cloud-c1.googledomains.com
104.197.215.107Likewise, there should be SOA records:
$ dig +short soa duh.to
ns-cloud-c1.googledomains.com. cloud-dns-hostmaster.google.com. 1 21600 3600 1209600 300Use an existing YubiKey:
$ ssh-add -L
ssh-rsa AAAAB4NzaC1yc2EAAAADAQABAAACAz[...]zreOKM+HwpkHzcy9DQcVG2Nw== cardno:000605553211Or create a new 4096-bit RSA key-pair to use for logging into the instance via SSH (pass-phrase is optional):
$ ssh-keygen -t rsa -b 4096 -C 'sysadm' -f ~/.ssh/duhWhere sysadm is the desired username on the instance.
Copy the public key:
$ cat ~/.ssh/duh.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/[...] sysadmEdit either instance or project-wide settings and paste the public key into the SSH Keys section:
Select Save.
On a client, edit ~/.ssh/config to use the new key:
Host duh
User sysadm
HostName duh.to
#HostName 104.197.215.107
IdentityFile ~/.ssh/duh
The first time you connect, you will see a warning about the host authenticity:
$ ssh duh
[...]
The authenticity of host 'duh' (104.197.215.107)' can't be established.
ECDSA key fingerprint is d6:9a:...:1d:c1.
Are you sure you want to continue connecting (yes/no)?To verify this fingerprint, you will need to check the instance Serial Console output, most likely using the Web UI.
See YubiKey Guide to secure SSH keys.
Install pending updates:
$ sudo apt-get update && sudo apt-get upgrade -yInstall any necessary software, for example:
$ sudo apt-get -y install \
zsh vim tmux dnsutils whois \
git gcc autoconf make \
lsof tcpdump htop tree \
apt-transport-httpsOptional Clone the configuration repository:
$ git clone https://github.com/drduh/configtmux is a terminal multiplexer. This program allows reconnecting to a working terminal session on the instance.
Use my configuration:
$ curl -o ~/.tmux.conf https://raw.githubusercontent.com/drduh/config/master/tmux.confRun tmux and open a new tab with `-c or specified keyboard shortcut.
`-1, `-2, `-3 switch to windows 1, 2, 3, etc.
`-d will disconnect from Tmux so you can save the session and log out.
When you reconnect to the instance, type tmux attach -t <session name> (or tmux a for short) to select a session to "attach" to (default name is "0"; use `-$ to rename).
Note If you're using the st terminal and receive the error open terminal failed: missing or unsuitable terminal: st-256color, copy the file st.info from st's build directory to the instance and run tic st.info.
Z shell is an interactive login shell with many features and improvements over Bourne shell.
To set the login shell for the current user to Zsh:
$ sudo chsh -s /usr/bin/zsh $USERUse my configuration:
$ curl -o ~/.zshrc https://raw.githubusercontent.com/drduh/config/master/zshrcOpen a new tmux tab and run zsh or start a new ssh session to make sure the configuration is working to your liking.
Vim is an excellent open source text editor. Run vimtutor if you have not used Vim before.
Use my configuration:
$ curl -o ~/.vimrc https://raw.githubusercontent.com/drduh/config/master/vimrc
$ mkdir -p ~/.vim/{backup,swap,undo}Take a few steps to harden remote access: declare which users are allowed to log in, change the default listening port and generate a new host key. There are many more in-depth guides online on securing SSH (1, 2, 3; these are just basic suggestions:
Create a new host RSA keys (do not use a pass-phrase - else you won't be able to connect remotely after a reboot):
$ ssh-keygen -t rsa -b 4096 -f ssh_host_key -C '' -N ''Move them into place and lock down file permissions:
$ sudo mv ssh_host_key ssh_host_key.pub /etc/ssh/
$ sudo chown root:root /etc/ssh/ssh_host_key /etc/ssh/ssh_host_key.pub
$ sudo chmod 0600 /etc/ssh/ssh_host_keyUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/sshd_config | sudo tee /etc/ssh/sshd_configUpdate Networking firewall rules to allow the new ssh listening port (for example, my SSHD configuration uses port 2222):
$ gcloud compute firewall-rules create ssh-tcp-2222 --network $NETWORK --allow tcp:2222 --source-ranges $(curl -s https://icanhazip.com)Do not exit the current ssh session yet; first make sure you can still connect!
Restart ssh server:
$ sudo service ssh restartOn a client, edit ~/.ssh/config to make any modifications, for example by adding Port 2222:
Host duh
User sysadm
HostName duh.to
IdentityFile ~/.ssh/duh
Port 2222
Start a new ssh session to confirm it works, then exit the other session.
If you had created a new host key, you'll be asked to verify the new key fingerprint:
$ ssh duh
The authenticity of host '[104.197.215.107]:2222 ([104.197.215.107]:2222)' can't be established.
RSA key fingerprint is 19:de:..:fe:58:3a.
Are you sure you want to continue connecting (yes/no)? yesTo check the sha256 fingerprint of the host key from the established session:
$ ssh-keygen -E sha256 -lf /etc/ssh/ssh_host_key.pub
4096 SHA256:47DEQpj8HBSa+/TImW+6JCeuQfRkm5NMpJWZG3hSuFU no comment (RSA)To check the md5 fingerprint of the host key:
$ ssh-keygen -E md5 -lf /etc/ssh/ssh_host_key.pub
4096 19:de:..:fe:58:3a /etc/ssh/ssh_host_key.pub (RSA)GNU Privacy Guard is used to verify signatures for downloaded software, encrypt and decrypt files, text, email, and much more.
Use my configuration:
$ mkdir ~/.gnupg ; curl -o ~/.gnupg/gpg.conf https://raw.githubusercontent.com/drduh/config/master/gpg.confTo use GPG to symmetrically encrypt a directory into a single file:
$ tar zcvf - ~/backup | gpg -c > ~/backup-$(date +%F-%H%M).tar.gz.gpgTo decrypt the file and unpack the directory:
$ gpg -o ~/decrypted-backup.tar.gz -d backup-2016-01-01-0000.tar.gz.gpg && tar zxvf ~/decrypted-backup.tar.gzSee YubiKey Guide to learn more about using GPG.
Dnsmasq is a lightweight DNS and DHCP server with many useful features.
Install Dnsmasq:
$ sudo apt-get -y install dnsmasqUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/dnsmasq.conf | sudo tee /etc/dnsmasq.confPick an upstream name server. To use Google resolvers, add server=169.254.169.254 to /etc/dnsmasq.conf or use a resolv-file:
$ echo "nameserver 169.254.169.254" | sudo tee /etc/resolv.dnsmasq
nameserver 169.254.169.254Optional Install a DNS blocklist (alternative method), for example:
$ curl https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | sudo tee /etc/dns-blocklistAppend any additional lists, for example:
$ curl https://raw.githubusercontent.com/Sinfonietta/hostfiles/master/social-hosts | sudo tee --append /etc/dns-blocklistCheck the file length and that no non-localhost addresses were appended:
$ wc -l /etc/dns-blocklist
66290 /etc/dns-blocklist
$ grep -ve "^127.0.0.1\|^0.0.0.0\|^#" /etc/dns-blocklist | sort | uniq
::1 ip6-localhost
::1 ip6-loopback
::1 localhost
255.255.255.255 broadcasthostRestart the service:
$ sudo service dnsmasq restartCheck the log to make sure it is running:
$ sudo tail -F /var/log/dnsmasq
started, version 2.76 cachesize 2000
compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect inotify
using nameserver 8.8.8.8#53
using nameserver 8.8.4.4#53
read /etc/hosts - 6 addresses
read /etc/dns-blocklist - 63894 addressesIf it fails to start, try running it manually:
$ sudo dnsmasq -C /etc/dnsmasq.conf -d
dnsmasq: started, version 2.76 cachesize 2000
dnsmasq: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect inotify
dnsmasq: using nameserver 8.8.8.8#53
dnsmasq: using nameserver 8.8.4.4#53
dnsmasq: read /etc/hosts - 6 addresses
dnsmasq: read /etc/dns-blocklist - 63894 addressesQuery locally for an A record to confirm dnsmasq is working:
$ dig +short a google.to @127.0.0.1
74.125.202.105
74.125.202.103
74.125.202.104
74.125.202.99DNSCrypt software can be used as a server and client to encrypt DNS traffic, as well as filter and shape queries.
If you are running your own private or public recursive DNS server, adding support for the DNSCrypt protocol requires installing DNSCrypt-Wrapper, the server-side DNSCrypt proxy.
To configure a private or public DNSCrypt server, first install libsodium and libevent:
$ sudo apt-get -y install libsodium-dev libevent-devClone the DNSCrypt-Wrapper repository, make and install the software:
$ git clone --recursive git://github.com/Cofyc/dnscrypt-wrapper.git
$ cd dnscrypt-wrapper
$ make configure
$ ./configure
$ sudo make installCreate keys and certificate (see usage instructions on Cofyc/dnscrypt-wrapper for details):
$ mkdir ~/dnscrypt-keys && cd ~/dnscrypt-keys
$ dnscrypt-wrapper --gen-provider-keypair \
--provider-name=2.dnscrypt.cloud --ext-address=$(curl -s https://icanhazip.com/)
Generate provider key pair... ok.
[...]
Keys are stored in public.key & secret.key.Save the stamp (sdns:\\...) parameter and possibly others for older client versions. To use a port other than 443, use https://dnscrypt.info/stamps to update it.
$ dnscrypt-wrapper --gen-crypt-keypair --crypt-secretkey-file=1.key
Generate crypt key pair... ok.
Secret key stored in 1.keyBy default, keys expire after 24 hours - 8 days are specified in the command below:
$ dnscrypt-wrapper --gen-cert-file --crypt-secretkey-file=1.key \
--provider-cert-file=1.cert --provider-publickey-file=public.key \
--provider-secretkey-file=secret.key --cert-file-expire-days=8
[20300] 01 May 00:00:00.000 [notice] [main.c:405] Generating pre-signed certificate.
[20300] 01 May 00:00:00.000 [notice] [main.c:412] TXT record for signed-certificate:
[...]
[20300] 01 May 00:00:00.000 [notice] [main.c:566] Certificate stored in 1.cert.Start the server on port 5355:
$ sudo dnscrypt-wrapper --resolver-address=127.0.0.1:53 \
--listen-address=0.0.0.0:5355 --provider-name=2.dnscrypt.cloud \
--crypt-secretkey-file=1.key --provider-cert-file=1.cert -VNote The provider-name parameter is not encrypted during the connection handshake.
The steps to generate dnscrypt-wrapper keys and start the server can be automated with a script like drduh/config/scripts/dnscrypt.sh.
Update Networking firewall rules to allow the new dnscrypt listening port (in this example, UDP port 5355).
$ gcloud compute firewall-rules create dnscrypt-udp-5355 --network $NETWORK --allow udp:5355 --source-ranges $(curl -s https://icanhazip.com)To connect from a client, edit dnscrypt-proxy.toml to include the static server stamp:
listen_addresses = ['127.0.0.1:40']
server_names = ['abc']
[static]
[static.'abc']
stamp = 'sdns://AQAAAAAAAAAAEj...ZA'
See drduh/config/dnscrypt-proxy.toml and jedisct1/dnscrypt-proxy/example-dnscrypt-proxy.toml for more options.
Start the client manually:
$ sudo ./dnscrypt-proxyCheck the logfile:
$ tail -f dnscrypt.log
[NOTICE] dnscrypt-proxy 2.0.19
[NOTICE] Loading the set of blocking rules from [blacklist.txt]
[NOTICE] Loading the set of forwarding rules from [forwarding-rules.txt]
[NOTICE] Loading the set of IP blocking rules from [ip-blacklist.txt]
[NOTICE] Now listening to 127.0.0.1:4002 [UDP]
[NOTICE] Now listening to 127.0.0.1:4002 [TCP]
[NOTICE] [abc] OK (crypto v1) - rtt: 52ms
[NOTICE] Server with the lowest initial latency: abc (rtt: 52ms)
[NOTICE] dnscrypt-proxy is ready - live servers: 1Or install and start it as a service:
$ sudo ./dnscrypt-proxy -service install
$ sudo ./dnscrypt-proxy -service startOutgoing DNS packets will now be encrypted from the client.
For example, take a packet capture on the client while running dig a google.to @127.0.0.1 -p 40 in another terminal:
$ sudo tcpdump -As80 -tni eth0 "udp port 5355"
listening on eth0, link-type EN10MB (Ethernet), capture size 80 bytes
IP 10.8.4.2.55555 > 104.197.215.107:5355: UDP, length 512
E... [email protected]
..%h.x}._.......G.....%.....0......bOF.".#%...ZA.T...
IP 104.197.215.107.5355 > 10.8.4.2.55555: UDP, length 304
E..L.E..)...h.x}
..%..._.8..r6fnvWj84'TQ.&.. O....&..>
.P|y.%.....
^CCompare with querying Google Public DNS directly with dig a google.to @8.8.8.8 while listening on UDP port 53:
$ sudo tcpdump -As80 -tni eth0 "udp port 53"
listening on eth0, link-type EN10MB (Ethernet), capture size 80 bytes
IP 10.8.4.2.55555 > 8.8.8.8.53: 45279+ [1au] A? google.to. (38)
[email protected]
..%.....t.5...|... .........google.to.......)........
IP 8.8.8.8.53 > 10.8.4.2.55555: 45279 1/0/1 (54)
E..R*...4.=.....
..%.5.t.>...............google.to..............+.
^COnce DNSCrypt is configured on the client, edit /etc/dnsmasq.conf and append server=127.0.0.1#40 to use the local port for DNSCrypt.
DNSCrypt supports query blocking with regular expression matching.
On the client, clone the dnscrypt-proxy repository and use the included Python script to generate a list, then configure dnscrypt to use it.
$ git clone https://github.com/jedisct1/dnscrypt-proxy
$ cd dnscrypt-proxy/utils/generate-domains-blacklists
$ python2 generate-domains-blacklist.py > blacklist
Loading data from [file:domains-blacklist-local-additions.txt]
Loading data from [https://easylist-downloads.adblockplus.org/antiadblockfilters.txt]
Loading data from [https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt]
[...]
Loading data from [https://raw.githubusercontent.com/notracking/hosts-blocklists/master/domains.txt]
Loading data from [file:domains-time-restricted.txt]
Loading data from [file:domains-whitelist.txt]
$ mv blacklist ~/build/linux-x86_64/blacklist.txt
$ wc -l blacklist.txt
117838 blacklist.txtPrivoxy is a non-caching web proxy with advanced filtering capabilities for enhancing privacy, modifying web page data and HTTP headers, controlling access, and removing ads and other obnoxious Internet junk.
Install Privoxy on the server:
$ sudo apt-get -y install privoxyUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/privoxy/config | sudo tee /etc/privoxy/config
$ curl https://raw.githubusercontent.com/drduh/config/master/privoxy/user.action | sudo tee /etc/privoxy/user.actionRestart Privoxy:
$ sudo service privoxy restartTest Privoxy locally on the server:
$ ALL_PROXY=127.0.0.1:8118 curl -I http://p.p/
HTTP/1.1 200 OK
Content-Length: 2500
Content-Type: text/html
Cache-Control: no-cache
Date: Sun, 01 May 2016 00:00:00 GMT
Last-Modified: Sun, 01 May 2016 00:00:00 GMT
Expires: Sat, 17 Jun 2000 12:00:00 GMT
Pragma: no-cacheClients can use the remote proxy with Secure Shell tunneling, also known as a "poor man's VPN" (Note AllowTcpForwarding yes must be enabled in /etc/ssh/sshd_config on the server to use these features, followed by sudo service ssh restart).
$ ssh -NCL 5555:127.0.0.1:8118 duhIn another client terminal:
$ ALL_PROXY='127.0.0.1:5555' curl https://icanhazip.com/
104.197.215.107Requests will appear in Privoxy logs if logging is enabled:
$ sudo tail -F /var/log/privoxy/logfileOr to use ssh as a SOCKS proxy:
$ ssh -NCD 7000 duhIn another client terminal:
$ curl --proxy socks5h://127.0.0.1:7000 https://icanhazip.com/
104.197.215.107Tor can be used as a public relay or as a private bridge for you and your friends.
Install Tor on the server - by default Tor does not relay nor exit traffic; it only provides a local port for outbound connections.
$ sudo apt-get -y install torOptional Install and configure anonymizing relay monitor (arm), a terminal-based status monitor for Tor.
$ sudo apt-get -y install tor-arm
$ sudo armUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/torrc | sudo tee /etc/tor/torrcTor can listen locally to resolve DNS A, AAAA and PTR records anonymously. To use, add a local address to /etc/tor/torrc:
DNSPort 127.26.255.1:53
Then append server=127.26.255.1 to /etc/dnsmasq.conf and restart both services.
Additionally, obfuscate Tor traffic by using obfsproxy or some other Tor pluggable transport.
To install the latest version of obfs4proxy, first install Golang:
$ sudo apt-get -y install golangCreate a temporary download and build directory:
$ export GOPATH=$(mktemp -d) ; echo $GOPATH
/tmp/tmp.u40VUD66nPDownload and build obfs4proxy:
$ go get git.torproject.org/pluggable-transports/obfs4.git/obfs4proxyNote If this fails for any reason, you likely need a more recent version of Go:
$ go get git.torproject.org/pluggable-transports/obfs4.git/obfs4proxy
# github.com/refraction-networking/utls
/tmp/tmp.s8JQjim8W7/src/github.com/refraction-networking/utls/tls.go:106: undefined: time.Until
# golang.org/x/net/http2
/tmp/tmp.s8JQjim8W7/src/golang.org/x/net/http2/server.go:214: h1.IdleTimeout undefined (type *http.Server has no field or method
IdleTimeout)
/tmp/tmp.s8JQjim8W7/src/golang.org/x/net/http2/server.go:215: h1.IdleTimeout undefined (type *http.Server has no field or method
[...]
$ go version
go version go1.7.4 linux/amd64
$ curl -O https://dl.google.com/go/go1.12.linux-amd64.tar.gz
$ sha256sum go1.12.linux-amd64.tar.gz
750a07fef8579ae4839458701f4df690e0b20b8bcce33b437e4df89c451b6f13
$ sudo tar -C /usr/local -xzf go1.12.linux-amd64.tar.gz
$ /usr/local/go/bin/go version
go version go1.12 linux/amd64
$ /usr/local/go/bin/go get git.torproject.org/pluggable-transports/obfs4.git/obfs4proxy
$ echo $?
0Confirm it's built and install it:
$ $GOPATH/bin/obfs4proxy -version
obfs4proxy-0.0.10-dev
$ sudo service tor stop
$ sudo cp $GOPATH/bin/obfs4proxy /usr/local/binSecure it:
$ sudo chown root:root /usr/local/bin/obfs4proxyEdit /etc/tor/torrc to include:
ORPort 9993
ExtORPort auto
BridgeRelay 1
ServerTransportPlugin obfs4 exec /usr/local/bin/obfs4proxy
ServerTransportListenAddr obfs4 0.0.0.0:10022
Restart Tor:
$ sudo service tor restartEnsure obfs4proxy is accepting connections:
$ sudo lsof -Pni | grep obfs
obfs4prox 6507 debian-tor 3u IPv6 62584 0t0 TCP *:10022 (LISTEN)Ensure connections from the server over Tor are possible:
$ curl --socks5 127.0.0.1:9050 https://icanhazip.com/
[tor exit node ip address]Update Networking firewall rules to allow the new proxy listening port (in this case, TCP port 10022):
$ gcloud compute firewall-rules create obfs4-tcp-10022 --network $NETWORK --allow tcp:10022 --source-ranges $(curl -s https://icanhazip.com)If Tor did not start, try starting it manually (sudo may be required to bind to privileged ports):
$ tor -f /etc/tor/torrc
[notice] Opening Socks listener on 127.0.0.1:9050
[notice] Opening OR listener on 0.0.0.0:9993
[notice] Opening Extended OR listener on 127.0.0.1:0
Extended OR listener listening on port 50161.
[...]
Bootstrapped 0%: Starting
Bootstrapped 5%: Connecting to directory server
Bootstrapped 45%: Asking for relay descriptors
Bootstrapped 78%: Loading relay descriptors
Registered server transport 'obfs4' at '[::]:10022'
Guessed our IP address as 104.197.215.107 (source: 62.210.222.166).
We now have enough directory information to build circuits.
Bootstrapped 80%: Connecting to the Tor network
Bootstrapped 90%: Establishing a Tor circuit
Tor has successfully opened a circuit. Looks like client functionality is working.
Bootstrapped 100%: DoneCopy the bridgeline, filling in the IP address and port:
$ sudo tail -n1 /var/lib/tor/pt_state/obfs4_bridgeline.txt
Bridge obfs4 <IP ADDRESS>:<PORT> <FINGERPRINT> cert=4ar[...]8FA iat-mode=0
$ sudo tail -n1 /var/lib/tor/pt_state/obfs4_bridgeline.txt | awk '{print $1,$2,"104.197.215.107:10022",$(NF-1),$(NF)}'
Bridge obfs4 104.197.215.107:10022 cert=4ar[...]8FA iat-mode=0To connect from a client, edit torrc to use the IP address and assigned port, for example:
UseBridges 1
Bridge obfs4 104.197.215.107:10022 cert=4ar[...]8FA iat-mode=0
Using Tor Browser, select Configure and Enter custom bridges:
Optional To host an onion service, append something like this to /etc/tor/torrc on the server (for example, to use with a Web server):
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 80 127.0.0.1:80
Restart Tor:
$ sudo service tor restartGet the service hostname:
$ sudo cat /var/lib/tor/hidden_service/hostname
pqccxgxxxxxxxl5h.onionYou can also host services like ssh as a onion service.
To generate a specific .onion hostname, some software exists.
Create your own public-key infrastructure, so that you may use your own keys and certificates for VPN, HTTPS, etc.
To create a certificate authority, intermediate authority, server and client certificates, download the following script.
It is recommended running the script to generate keys client-side, in a trusted computing environment, preferably air-gapped.
$ mkdir ~/pki && cd ~/pki
$ curl -o ~/pki/pki.sh https://raw.githubusercontent.com/drduh/config/master/scripts/pki.shRead through and edit the script and variables, especially CN_ ones, to your suit your needs:
$ vim pki.shMake the script executable:
$ chmod +x pki.shChange OpenSSL certificate requirements to disable mandatory location fields:
$ sudo sed -i.bak "s/= match/= optional/g" /usr/lib/ssl/openssl.cnfRun the script, accepting prompts with y to sign certificates and commit changes:
$ ./pki.sh
Generating RSA private key, 4096 bit long modulus
........................................................................++
.....................................++
[...]
Sign the certificate? [y/n]:yIf there were no errors, the script created private and public keys for a certificate authority, an intermediate certificate authority, a server and a client - along with certificate request (srl) and configuration files (cnf).
To check a certificate file (.pem extension) with OpenSSL:
$ openssl x509 -in ca.pem -noout -subject -issuer -enddate
subject=CN = Example Authority
issuer=CN = Example Authority
notAfter=Dec 1 00:00:00 2018 GMTYou could also use OpenVPN/easy-rsa or Let's Encrypt.
OpenVPN is free, open source TLS-based VPN server and client software.
Starting with the client, install OpenVPN:
$ sudo apt-get -y install openvpnUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/server.ovpn | sudo tee /etc/openvpn/server.ovpnPreferably on the client-side (where there is likely more entropy), generate a static key so that only trusted clients can attempt connections (extra authentication on top of TLS):
$ openvpn --genkey --secret ta.keyAlso client-side, create Diffie-Hellman key exchange parameters:
$ openssl dhparam -dsaparam -out dh.pem 4096Copy these files and certificates from the previous section to the server (note, the only private key sent is for the server itself):
$ scp ta.key dh.pem ca.pem intermediate.pem server.pem server.key duh:~On the server-side, move the files into place:
$ sudo mkdir /etc/pki
$ cat ca.pem intermediate.pem > chain.pem
$ sudo cp chain.pem server.pem server.key dh.pem ta.key /etc/pkiEnable IP forwarding and make the change permanent:
$ sudo sysctl -w net.ipv4.ip_forward=1
$ echo "net.ipv4.ip_forward = 1" | sudo tee --append /etc/sysctl.confCreate a NAT for VPN clients:
$ sudo iptables -t nat -A POSTROUTING -o eth0 -s 10.8.0.0/16 -j MASQUERADEOptional Route all HTTP (TCP port 80) traffic through Privoxy.
$ sudo iptables -t nat -A PREROUTING --source 10.8.0.0/16 -p tcp -m tcp --dport 80 -j DNAT --to 10.8.0.1:8118Make the firewall rules permanent:
$ sudo apt-get -y install iptables-persistent
$ sudo iptables-save | sudo tee /etc/iptables/rules.v4Append 10.8.0.1 as a Dnsmasq listening address and restart the services:
$ sudo sed -i.bak "s/listen-address=127.0.0.1/listen-address=127.0.0.1,10.8.0.1/g" /etc/dnsmasq.conf
$ sudo service dnsmasq restart
$ sudo service openvpn restartCheck the OpenVPN log:
$ sudo tail -F /var/log/openvpn.log
TUN/TAP device tun0 opened
TUN/TAP TX queue length set to 100
do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
/sbin/ip link set dev tun0 up mtu 1500
/sbin/ip addr add dev tun0 10.8.0.1/24 broadcast 10.8.0.255
UDPv4 link local (bound): [undef]
UDPv4 link remote: [undef]
v=256
IFCONFIG POOL: base=10.8.0.2 size=252, ipv6=0
Initialization Sequence CompletedIf it fails, try to start OpenVPN server manually:
$ sudo openvpn --config /etc/openvpn/server.ovpn --verb 3 --suppress-timestamps
OpenVPN 2.4.0 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 18 2017
library versions: OpenSSL 1.0.2l 25 May 2017, LZO 2.08
Diffie-Hellman initialized with 4096 bit key
Control Channel Authentication: using '/etc/pki/ta.key' as a OpenVPN static key file
Outgoing Control Channel Authentication: Using 256 bit message hash 'SHA256' for HMAC authentication
Incoming Control Channel Authentication: Using 256 bit message hash 'SHA256' for HMAC authentication
ROUTE_GATEWAY 10.240.0.1
TUN/TAP device tun0 opened
TUN/TAP TX queue length set to 100
do_ifconfig, tt->did_ifconfig_ipv6_setup=1
/sbin/ip link set dev tun0 up mtu 1500
/sbin/ip addr add dev tun0 10.8.0.1/24 broadcast 10.8.0.255
/sbin/ip -6 addr add 2001:db8:123::1/64 dev tun0
/sbin/ip route add 10.8.0.0/24 via 10.8.0.2
[...]
Initialization Sequence CompletedIf OpenVPN still fails due to unknown ciphers, you may need to install a newer OpenVPN server version - see OpenvpnSoftwareRepos.
Update the remote instance's firewall rules to allow the new VPN listening port (in this case, UDP port 443)
For each connecting device, edit a client configuration:
$ mkdir ~/vpn
$ vim ~/vpn/client.ovpnTo use my configuration:
$ curl -o ~/vpn/client.ovpn https://raw.githubusercontent.com/drduh/config/master/client.ovpnAdd the CA certificate, client certificate and client key material to the configuration:
$ (echo "<ca>" ; cat ~/pki/ca.pem ; echo "</ca>\n<cert>" ; cat ~/pki/client.pem; echo "</cert>\n<key>" ; cat ~/pki/client.key ; echo "</key>") >> client.ovpnFrom a client, copy ta.key from the server:
$ scp duh:~/pki/ta.key ~/vpnTo connect, install and start OpenVPN:
$ sudo apt-get -y install openvpn
$ cd ~/vpn
$ sudo openvpn --config client.ovpn
[...]
TLS: Initial packet from [AF_INET]104.197.215.107:443, sid=6901c819 3e11276e
VERIFY OK: depth=2, CN=Duh Authority
VERIFY OK: depth=1, CN=Duh Intermediate Authority
Validating certificate key usage
++ Certificate has key usage 00a0, expects 00a0
VERIFY KU OK
Validating certificate extended key usage
++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
VERIFY EKU OK
VERIFY OK: depth=0, CN=duh.to
[...]Verify your local IP address is the same as the server:
$ curl -4 https://icanhazip.com/
104.197.215.107Note If IPv6 is disabled, the connection may fail - you'll need to disable these options on the server to connect:
#server-ipv6 2001:db8:123::/64
#push "route-ipv6 2000::/3"
To connect from Android, install OpenVPN Connect.
Copy client.ovpn and ta.key to a folder on the Android device, using a USB cable or by sharing the files through Google Drive, for example.
Select Import > Import Profile from SD card and select client.ovpn, perhaps in the Download folder.
If the profile was was successfully imported, select Connect.
Mac Install OpenVPN from Homebrew:
$ brew install openvpnStart OpenVPN:
$ sudo ~/homebrew/sbin/openvpn --config client.ovpn
OpenVPN 2.4.4 x86_64-apple-darwin16.7.0 [SSL (OpenSSL)] [LZO] [LZ4] [PKCS11] [MH/RECVDA] [AEAD] built on Oct 2 2017
[...]
TLS: Initial packet from [AF_INET]104.197.215.107:443, sid=db4ecf82 4e4e4c5b
VERIFY OK: depth=2, CN=Duh Authority
VERIFY OK: depth=1, CN=Duh Intermediate Authority
Validating certificate key usage
++ Certificate has key usage 00a0, expects 00a0
VERIFY KU OK
Validating certificate extended key usage
++ Certificate has EKU (str) TLS Web Server Authentication, expects TLS Web Server Authentication
VERIFY EKU OK
VERIFY OK: depth=0, CN=duh.to
[...]
Initialization Sequence CompletedVerify traffic is routed through the remote server:
$ curl https://icanhazip.com/
104.197.215.107See macOS-Security-and-Privacy-Guide#vpn.
Optional You may want to run a Web server to serve static or dynamic pages.
Install Lighttpd with ModMagnet:
$ sudo apt-get -y install lighttpd lighttpd-mod-magnetUse my configuration:
$ curl https://raw.githubusercontent.com/drduh/config/master/lighttpd/lighttpd.conf | sudo tee /etc/lighttpd/lighttpd.conf
$ curl https://raw.githubusercontent.com/drduh/config/master/lighttpd/magnet.luau | sudo tee /etc/lighttpd/magnet.luauNote Lighttpd expects the server private key and certificate to be stored in one file as the ssl.pemfile argument:
$ sudo cat /etc/pki/server.key /etc/pki/server.pem | sudo tee /etc/pki/lighttpd.pemYou may need to comment out the following line in /etc/lighttpd/lighttpd.conf in order to accept requests on Internet-facing interfaces:
#server.bind = "127.0.0.1"
Restart Lighttpd:
$ sudo service lighttpd restartCheck that it's running - look for the process listening on TCP ports 80 or 443:
$ sudo lsof -Pni | grep lighttpd
lighttpd 3291 www-data 4u IPv4 18206 0t0 TCP *:80 (LISTEN)
lighttpd 3291 www-data 5u IPv4 18207 0t0 TCP *:443 (LISTEN)If it failed to start, try running it directly to check for errors:
$ sudo lighttpd -f /etc/lighttpd/lighttpd.conf -DUpdate Networking firewall rules to allow the new HTTP/HTTPS listening port(s) (in this example, TCP port 80 and 443).
Create some content:
$ echo "Hello, World" | sudo tee /var/www/index.htmlOnce Lighttpd is running, request a page from the server in a Web browser or by using cURL:
$ curl -vv http://duh.to/
Hello, WorldYou can use client certificates as a means of authentication and authorization, rather than relying on user-provided passwords. See my Lighttpd configuration for an example.
Run your own XMPP chat server with Prosody. Client can use Off The Record (OTR) messaging, a form of secure messaging which includes encryption, authentication, deniability and perfect forward secrecy, to communicate privately.
Install Prosody:
$ sudo apt-get -y install prosodyUse my configuration and edit it to suit your needs:
$ sudo curl -o /etc/prosody/prosody.cfg.lua https://raw.githubusercontent.com/drduh/config/master/prosody.cfg.luaOr customize your own. See also Advanced ssl config.
Use Diffie-Hellman key exchange parameters from the Certificate steps:
$ sudo cp ~/pki/dh.pem /etc/pki/dh.pemCopy the server certificate and key from the Certificate steps:
$ sudo cp ~/pki/server.pem /etc/pki/xmpp-cert.pem
$ sudo cp ~/pki/server.key /etc/pki/xmpp-key.pemIf using a custom CA or intermediate certificate, append it to the server certificate, for example:
$ cd ~/pki && cat server.pem intermediate.pem ca.pem | sudo tee /etc/pki/xmpp-cert.pemOr generate a new self-signed certificate:
$ sudo openssl req -x509 -newkey rsa:4096 -days 365 -sha512 -subj "/CN=server.name" \
-nodes -keyout /etc/pki/xmpp-key.pem -out /etc/pki/xmpp-cert.pemSet file ownership:
$ sudo chown prosody:prosody /etc/pki/xmpp-*.pemRestart Prosody:
$ sudo service prosody restartEnsure it's running and listening:
$ sudo tail -n1 /var/log/prosody/prosody.log
mod_posix info Successfully daemonized to PID 1831
$ sudo lsof -Pni | grep prosody
lua5.1 1831 prosody 6u IPv6 317986 0t0 TCP *:5269 (LISTEN)
lua5.1 1831 prosody 7u IPv4 317987 0t0 TCP *:5269 (LISTEN)
lua5.1 1831 prosody 8u IPv6 317990 0t0 TCP *:5222 (LISTEN)
lua5.1 1831 prosody 9u IPv4 317991 0t0 TCP *:5222 (LISTEN)Update Networking firewall rules to allow the new prosody listening ports (in this example, TCP ports 5222 and 5269):
$ gcloud compute firewall-rules create xmpp-tcp-5222-5269 --network $NETWORK --allow tcp:5222,tcp:5269 --source-ranges $(curl -s https://icanhazip.com)Create a new user:
$ sudo prosodyctl adduser [email protected]Important The domain name must match the server certificate common name (CN_SERVER in pki.sh) - check with sudo openssl x509 -in /etc/pki/xmpp-cert.pem -noout -subject
For others to communicate with your XMPP server, you must configure DNS records for interdomain federation:
_xmpp-client._tcp of type SRV with data 0 5 5222 duh.to.
_xmpp-server._tcp of type SRV with data 0 5 5269 duh.to.
After a little while, check domain SRV records:
$ dig +short srv _xmpp-server._tcp.duh.to
0 5 5269 duh.to.
$ dig +short srv _xmpp-client._tcp.duh.to
0 5 5222 duh.to.To connect from a client, use Profanity
$ sudo apt-get -y install profanity
$ profanityLog in by typing /connect [email protected] and entering the password when prompted.
Generate OTR keys by typing /otr gen - this part may take a while.
Send a message to a contact by typing /msg [email protected] - to navigate tabs, use /win 1, /win 2, etc.
To start OTR, type /otr start - Profanity will show OTR session started (untrusted).
To authenticate the chat partner, type /otr question foo? bar where bar is an answer to foo? which only the person you assume to be speaking with can answer. If the person answers correctly, Profanity will show Authentication successful followed by OTR session trusted - now you can be sure the connection is encrypted and authenticated.
Or use agl/xmpp-client:
$ go get github.com/agl/xmpp-client
$ $GOPATH/bin/xmpp-clientIf you can't connect, check for errors in /var/log/prosody/prosody.err on the server.
To verify the SHA256 fingerprint matches the certificate on the server:
$ openssl x509 -in /etc/pki/xmpp-cert.pem -fingerprint -noout -sha256To view and verify the XMPP server's certificate fingerprint remotely, use the openssl command from a client:
$ echo -e | openssl s_client -connect duh.to:5222 -starttls xmpp | openssl x509 -noout -fingerprint -sha256 | tr -d ':'
[...]
SHA256 Fingerprint=9B759D41E3DE30F9D2F902027D792B65D950A98BBB6D6D56BE7F2528453BF8E9Note If using agl/xmpp-client and custom certificates (i.e., not signed by a trusted root CA), you will need to manually add the server's SHA256 fingerprint to ~/.xmpp-client, like:
"ServerCertificateSHA256": "9B759D41E3DE30F9D2F902027D792B65D950A98BBB6D6D56BE7F2528453BF8E9"
If an error occurs while attempting to connect, check /var/log/prosody/prosody.err on the server.
Reboot the instance and make sure everything still works. If not, you'll need to automate certain programs to start up on their own (for example, Privoxy will fail to start if OpenVPN does not first create a tunnel interface to bind to).
With this guide, a secure server with several privacy and security enchancing services can be setup in less than an hour. The server can be used to circumvent firewalls, provide strong encryption and overall improve online experience, all for a low monthly cost (average ~$35 per month for a "standard" instance.) To save money, consider using Preemptible VM instances which can be started right back up with a script.




