Making a website only accessible through a VPN

Hello,

I have a web app that I’m hosting on an aws VM. I would like to have this app only accessible by specific people. I do not want it exposed to the public.

In my mind it should be like an extension of a local network. Assume the VM is the local network and only users on that network can access the webserver on hosted by the VM.

I want to have it accessible by about 15-25 people.

Does this make sense?

I am not knowledgeable about VPNs. I’ve been reading about OpenVPN, CloudFlare tunnel, and Wireguard. I’m a bit overwhelmed.

I want something that’s easy for the end users to set up. (also, free haha, but if the price is reasonable then sure)

Any suggestions?

ZeroTier and WireGuard will both work and be fairly easy to configure for this

If you use reverse proxy like Nginx, you can just block every IP, and open local and VPN IP range up. It’s just 2 - 3 lines in the config.

https://www.cyberciti.biz/faq/linux-unix-nginx-access-control-howto/

# allow anyone in 192.168.1.0/24
allow   192.168.1.0/24;
# drop rest of the world
deny    all;

Probably works similar with other proxies.

I have zerotier setup on my network across a few devices, the way I do it is like this via the use of zerotier and DNS config:

externalApp.mydomain.com points to a real IP address of my server

internalApp.mydomain.com points to an internal zerotier IP address of the same server

E.g. My personal blog site mysite.com is accessible on the Internet, but to get to bitwarden.mysite.com you’d have to be connected to the Zerotier network first

This way, I don’t need to mess around with my reverse proxy to set up any specific rules. As long as my devices (phone / laptop / pc) are connected to zerotier, I can access my apps from anywhere. Zerotier is very easy to setup

Everyone’s ignoring that the thing you’re protecting is already in AWS. So shut off all external access to its VPC, add a (tiny) EC2 instance in its own VPC to run Wireguard and a reverse proxy, e.g. Caddy or nginx, allow UDP on port 51820 (or whatever you use) for wg only, nothing else, and enable peering between the VPCs.

You could also run dnsmasq if you want to (privately) resolve any domain names associated with these site(s) - just use the same server IP as the DNS in Wireguard config, and set it to resolve to its own IP. Then the reverse proxy can forward each hostname to the same, but use upstream DNS, and get the ‘real’ IP for it, the appropriate internal IP for the peering connection.

Probably no guide/blog for the whole of this exactly, but each piece independently for sure. Or I can try to elaborate a bit on anything specific if you want.

I was working on a similar setup, but I didn’t have the time to finish it yet.

But basically, I was using Wireguard as the VPN, and Nginx Proxy Manager as the proxy for my other apps. All of them are running on Docker.

On theory, you just have to create an internal network inside Docker, and put all of them in that network instead of the default one.

In your case, I would just deploy a Nginx as a reverse proxy with TLS/SSL termination and client certificates on the very same AWS VPS. Using easy-rsa scripts I would easily generate a CA and one or more SSL certificates and hand it/them over to the 15-25 people you want to permit the access to. They would import the certificate in their browser and only they would be able to access the web app.

The way I do it is using nginx. Any domain locations I don’t want public I have locked to my VPN and local subnets, if you’re not on those subnets you get hit with a 403 error.

It’s probably possible to do that with other web servers like apache, but Im not as familiar. I have a wireguard and zerotier VPN active and have those as allowed through to my web servers. Wireguard is probably the best option, but tailscale or zerotier are incredibly easy to setup for this purpose.

Netmaker if you want the fastest option using kernel WireGuard + self-hosting.

It’s pretty easy.

Step 1: Install tailscale everywhere,

Step 2: point your domain to a 100.x (tailscale IP of the webserver)

Step 3: there is no step 3

Now only those who have access to your tailscale account can reach that website

Bonus: you can setup a public subdomain if you want, for example public.mydomain.net or vpn.mydomain.net

What kind of website? Static content? Basic auth is not an option?

If the goal is just to prevent the public from accessing the site, wouldn’t HTTP basic auth be sufficient? Going the VPN route is obviously more secure but would require everyone in your group to install a VPN client, which is a bit more inconvenient

Setting up something like a wireguard VPN is quite easy. It creates a virtual interface to pass traffic through.

So when you have your webserver, you can bind it to that interface, making it the ONLY way to access the website.

For the users, you just have to issue a config file which is then easily imported for any device (iOS, Android, Linux, Mac, Windows…).

Get the IPs of the VPNs you want to be able to connect from, then have it so the website will reject any connections from ips not on the allow list or have an allow list set up in the firewall that blocks by default but allows connections from certain ips

I use Tailscale for this purpose, it is a good option too.

Ok thanks. I will research these some more.

I don’t know the VPN side of things either. So I’m looking for a starting point.

Ok a lot of people recommending wireguard. I’ll do some more reading on it.

This is the best answer. Only thing is that you might need some custom DNS if you want it to be accessible via an internal domain

The crud app still has basic auth (assuming you mean a user login) but I do not want it to be accessible by the public.