install.sh and refactored config check in

This commit is contained in:
Jamil Bou Kheir
2021-06-29 21:08:04 -07:00
parent f3c21ccdd9
commit 4bec2aa5e0
9 changed files with 205 additions and 65 deletions

View File

@@ -10,16 +10,20 @@ echo "Setting capabilities"
sudo setcap "cap_net_admin+ep" cloudfire
sudo setcap "cap_net_raw+ep" cloudfire
sudo setcap "cap_dac_read_search+ep" cloudfire
mkdir $HOME/.cache
chmod +x cloudfire
file cloudfire
# Create DB
sudo -i -u postgres psql -c "CREATE DATABASE cloudfire;"
mkdir $HOME/.cache
# Start by running migrations always
./cloudire eval "CfHttp.Release.migrate"
# Start in the background
./cloudfire &
# Wait for app to start
sleep 10
sleep 5
echo "Trying to load homepage..."
curl -i -vvv -k https://$(hostname):8800/

View File

@@ -1,3 +1,3 @@
erlang 24.0.2
erlang 24.0.3
elixir 1.12.1-otp-24
nodejs lts

View File

@@ -12,64 +12,63 @@ You have been warned.
1. [Intro](#intro)
2. [Architecture](#architecture)
3. [Setup](#setup)
3. [Install](#install)
4. [Usage](#usage)
5. [Contributing](#contributing)
## Intro
CloudFire is a host-it-yourself VPN and firewall configurable through a Web UI.
It aims to be a simple way to setup a VPN and optional firewall for all your
devices.
`cloudfire` is an open-source WireGuard™ VPN and firewall manager for Linux
designed to be easy-to-use, secure, and useful for individuals and small teams.
Use CloudFire to:
Use `cloudfire` to:
- Set up your own VPN
- Block, inspect, or capture outgoing traffic from your phone / tablet /
computer to any IP(s)
- Connect remote teams in a secure virtual LAN
- Set up your own WireGuard™ VPN
- Block egress traffic to specific IPs and CIDR ranges
- Configure DNS in one central place for all your devices
## Architecture
CloudFire is written in the Elixir programming language and composed as an [Umbrella
`cloudfire` is written in the Elixir programming language and composed as an [Umbrella
project](https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html)
consisting of three Elixir packages:
consisting of three independent applications:
- [apps/cf_http](apps/cf_http): The Web Application
- [apps/cf_wall](apps/cf_wall): Firewall Management Process
- [apps/cf_vpn](apps/cf_vpn): WireGuard™ Management Process
For now, CloudFire assumes these apps are all running on the same host.
For now, `cloudfire` assumes these apps are all running on the same host.
## Setup
## Install
`curl https://github.com/CloudFire-LLC/cloudfire/releases/download/latest/cloudfire-init.sh | sudo bash -E`
Prerequisites:
1. Postgresql Server 9.6 or higher. Access can be configured in
`~/.cloudfire/config.json` after installation.
2. `wg`, `openssl`, `ip`, and `iptables` must be in your PATH.
Then you can install `cloudfire` with:
`curl https://github.com/CloudFire-LLC/cloudfire/blob/master/install.sh | bash -`
This will download the `cloudfire` binary, initialize the config directory, and
print further instructions to the console.
## Creating additional admin users
You may create additional admin users with the following command:
```bash
vagrant up
```
This will download the VM base box, provision it with dependencies, bootstrap
the CloudFire DB, launch the CloudFire Services, and print instructions for
connecting to the Web UI.
## Creating Additional Users
CloudFire creates the first user for you upon installation and prints the
credentials after `vagrant up` completes in the step above.
You may create additional users with the following command:
```bash
sudo -u cloudfire /opt/cloudfire/bin/cloudfire rpc 'CfHttp.Users.create_user(
cloudfire rpc 'CfHttp.Users.create_user(
email: "USER_EMAIL",
password: "USER_PASSWORD",
password_confirmation: "USER_PASSWORD"
)'
```
This will create a user you can use to log into the Web UI.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md).
WireGuard™ is a registered trademark of Jason A. Donenfeld.

View File

@@ -0,0 +1,73 @@
defmodule CfCommon.ConfigFile do
@moduledoc """
Common config file operations.
"""
alias CfCommon.CLI
@base_path "$HOME/.cloudfire/"
@config_path @base_path <> "config.json"
@default_config %{
https_listen_port: "8800",
https_listen_address: "127.0.0.1",
wg_listen_port: "51820",
database_url: "ecto://postgres:postgres@localhost/cloudfire",
ssl_cert_file: @base_path <> "cert.pem",
ssl_key_file: @base_path <> "key.pem"
}
# Write then load, ensures clean slate
def init! do
Map.merge(@default_config, generate_config())
|> write!()
load!()
end
def load! do
%{} = Jason.decode!(File.read!(@config_path))
end
def write!(config) do
@config_path
|> File.write!(Jason.encode!(config), [:write])
end
def exists? do
File.exists?(@config_path)
end
defp generate_config do
%{
live_view_signing_salt: live_view_signing_salt(),
secret_key_base: secret_key_base(),
database_url: database_url(),
db_encryption_key: db_encryption_key(),
url_host: url_host(),
wg_server_key: wg_server_key()
}
end
defp live_view_signing_salt do
CLI.exec!("openssl rand -base64 24")
end
defp secret_key_base do
CLI.exec!("openssl rand -base64 48")
end
defp db_encryption_key do
CLI.exec!("openssl rand -base64 32")
end
defp url_host do
CLI.exec!("hostname")
end
defp database_url do
"ecto://postgres:postgres@127.0.0.1/cloudfire"
end
defp wg_server_key do
CLI.exec!("wg genkey")
end
end

View File

@@ -25,6 +25,7 @@ defmodule CfCommon.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:jason, "~> 1.2"}
# {:dep_from_hexpm, "~> 0.3.0"},
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"},
# {:sibling_app_in_umbrella, in_umbrella: true}

View File

@@ -1,17 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="1361px" height="346px" viewBox="0 0 1361 346" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>CloudFire logo</title>
<desc></desc>
<svg width="468px" height="100px" viewBox="0 0 468 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
<title>Slice</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="noun_extinguisher_2253371" transform="translate(41.000000, 0.000000)" fill="#000000">
<path d="M729.305286,118.111219 L752.001448,118.111219 C750.928918,119.016403 749.94361,120.020513 749.058504,121.110319 C744.072892,126.959498 741.204317,135.212924 737.882504,144.733051 C737.104936,146.960954 736.300925,149.280756 735.432048,151.669274 C735.425162,151.688591 735.061029,152.785847 734.339651,154.96104 C733.965611,156.083391 734.217604,157.320416 735.000704,158.206141 C735.783805,159.091865 736.979042,159.491727 738.136182,159.255102 C739.293321,159.018476 740.236566,158.181312 740.610605,157.058962 L741.658794,153.909182 C742.536761,151.494999 743.342837,149.122625 744.122472,146.887684 C750.583067,128.372324 754.141621,118.111219 773.456888,118.111219 L776.15772,118.111219 L776.15772,132.806606 C776.197034,134.699615 777.694213,136.23804 779.581997,136.32522 L783.594614,136.32522 L783.594614,143.051153 C767.982737,147.462201 757.187885,161.717151 757.152324,177.969049 L757.152324,271.642414 C757.166204,273.487746 758.653454,274.981538 760.495208,275 L826.57821,275 C828.427483,274.996953 829.937441,273.51788 829.982242,271.665596 C829.982242,271.251641 829.868623,270.835203 829.868623,270.471337 L829.868623,177.969049 C829.868623,161.559476 818.713281,147.660124 803.839493,143.178238 L803.839493,136.32522 L807.703786,136.32522 C809.52913,136.32522 810.863226,134.635871 810.863226,132.806606 L810.863226,125.976355 L853.418788,125.976355 L853.418788,130.45079 C853.42459,131.782847 854.223419,132.982823 855.448647,133.49998 L898.561149,151.794702 C898.970002,151.967649 899.409304,152.056748 899.853102,152.056735 C900.477489,152.056489 901.086769,151.864323 901.598707,151.506176 C902.502975,150.883142 903.029932,149.842946 902.998082,148.743857 L902.998082,81.315211 C903.035943,80.2185573 902.511103,79.1787511 901.607066,78.5593467 C900.703029,77.9399423 899.545521,77.8270822 898.539251,78.2602263 L855.440383,96.3421758 C854.213148,96.8584706 853.415852,98.0631609 853.418788,99.3967465 L853.418788,104.036763 L810.863226,104.036763 L810.863226,97.0409311 C810.897596,96.1739182 810.581871,95.3295959 809.987355,94.6986525 C809.39284,94.0677091 808.569681,93.7033623 807.703786,93.6878991 L779.581997,93.6878991 C777.725009,93.6978676 776.210267,95.1810941 776.15772,97.0409311 L776.15772,111.487946 L729.305286,111.487946 C727.479827,111.487946 726,112.970616 726,114.799582 C726,116.628548 727.479827,118.111219 729.305286,118.111219 Z M861,102.187899 L897,87 L897,144 L861,128.633454 L861,102.187899 Z M853,110 L853,119 L811,119 L811,110 L853,110 Z M823,177.910604 L823,269 L764,269 L764,177.910604 C764,161.417752 777.297944,148 793.5,148 C809.702056,148 823,161.417752 823,177.910604 Z M797,142 C795.767017,141.858809 794.52811,141.788038 793.288353,141.787976 C792.19028,141.781382 791.092648,141.83776 790,141.956877 L790,136 L797,136 L797,142 Z M782,100 L803,100 L803,129 L782,129 L782,100 Z" id="Shape" stroke="#000000" stroke-width="10" fill-rule="nonzero"></path>
<text id="cloud" font-family="Helvetica-Light, Helvetica" font-size="288" font-weight="300">
<tspan x="0" y="280">cloud</tspan>
</text>
<text id="ire" font-family="Helvetica-Light, Helvetica" font-size="288" font-weight="300">
<tspan x="916" y="280">ire</tspan>
<g id="Group">
<text id="CloudFire" font-family="Copperplate" font-size="64" font-weight="normal" fill="#000000">
<tspan x="130" y="81">CloudFire</tspan>
</text>
<g id="logo">
<path d="M78,0 C106.929245,20.432184 76.9805386,65.7158066 87.5490618,84 C65.8312154,57.5738976 95.4207963,34.8637635 78,0 Z" id="path6" fill="#CF5C00"></path>
<path d="M91.1443025,40 C107.331715,50.3549561 87.9008034,74.6173904 98.2451105,80.8279448 C108.720702,87.1179806 107.535309,60.9772209 122,67.6830933 C106.939449,62.6385221 113.187017,93.7609844 94.5259937,89.6192304 C73.1703642,84.8793984 99.7289816,51.4368582 91.1443394,40 L91.1443025,40 Z" id="path8" fill="#8D1E00"></path>
<path d="M0,74.1842366 C30.309935,49.1402806 71.5956101,89.3287704 91.0782799,91.8400369 C116.133529,95.0690547 109.459363,65.0262052 122,66.1455494 C110.444409,67.5907024 117.380874,99.0104528 91.5454264,99.9814113 C63.4097368,101.038934 35.3390569,56.5526297 5.18418077e-05,74.1842366 L0,74.1842366 Z" id="path10" fill="#2B1200"></path>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -3,37 +3,45 @@
# although such is generally not recommended and you have to
# remember to add this file to your .gitignore.
import Config
alias CfCommon.ConfigFile
config_file =
if ConfigFile.exists?() do
ConfigFile.load!()
else
ConfigFile.init!()
end
# Required environment variables
database_url = System.fetch_env!("DATABASE_URL")
secret_key_base = System.fetch_env!("SECRET_KEY_BASE")
live_view_signing_salt = System.fetch_env!("LIVE_VIEW_SIGNING_SALT")
ssl_cert_file = System.fetch_env!("SSL_CERT_FILE")
ssl_key_file = System.fetch_env!("SSL_KEY_FILE")
database_url = Map.fetch!(config_file, "database_url")
secret_key_base = Map.fetch!(config_file, "secret_key_base")
live_view_signing_salt = Map.fetch!(config_file, "live_view_signing_salt")
ssl_cert_file = Map.fetch!(config_file, "ssl_cert_file")
ssl_key_file = Map.fetch!(config_file, "ssl_key_file")
disable_signup =
case System.get_env("DISABLE_SIGNUP") do
case config_file["disable_signup"] do
d when d in ["1", "yes"] -> true
_ -> false
end
ssl_ca_cert_file =
case System.get_env("SSL_CA_CERT_FILE") do
case config_file["ssl_ca_cert_file"] do
"" -> nil
s = _ -> s
end
default_egress_address =
CfCommon.CLI.exec!("ip route get 8.8.8.8 | grep -oP 'src \\K\\S+'")
CLI.exec!("ip route get 8.8.8.8 | grep -oP 'src \\K\\S+'")
|> String.trim()
# Optional environment variables
pool_size = max(:erlang.system_info(:logical_processors_available), 10)
queue_target = 500
https_listen_port = String.to_integer(System.get_env("HTTPS_LISTEN_PORT", "8800"))
wg_listen_port = System.get_env("WG_LISTEN_PORT", "51820")
wg_endpoint_address = System.get_env("WG_ENDPOINT_ADDRESS", default_egress_address)
url_host = System.get_env("URL_HOST", "localhost")
https_listen_port = String.to_integer(Map.get(config_file, "https_listen_port", "8800"))
wg_listen_port = Map.get(config_file, "wg_listen_port", "51820")
wg_endpoint_address = Map.get(config_file, "wg_endpoint_address", default_egress_address)
url_host = Map.get(config_file, "url_host", "localhost")
config :cf_http,
disable_signup: disable_signup
@@ -65,7 +73,7 @@ config :cf_http, CfHttpWeb.Endpoint,
config :cf_vpn,
vpn_endpoint: wg_endpoint_address <> ":" <> wg_listen_port,
private_key: File.read!("/opt/cloudfire/server.key") |> String.trim()
private_key: Map.fetch!(config_file, "wg_server_key") |> String.trim()
# ## Using releases (Elixir v1.9+)
#
@@ -85,11 +93,7 @@ config :cf_http, CfHttp.Vault,
#
# In Cloak 2.0, this will be the default iv length for AES.GCM.
tag: "AES.GCM.V1",
key: Base.decode64!(System.fetch_env!("DB_ENCRYPTION_KEY")),
key: Base.decode64!(Map.fetch!(config_file, "db_encryption_key")),
iv_length: 12
}
]
#
# Then you can assemble a release by calling `mix release`.
# See `mix help release` for more information.

57
install.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -e
# 1. Detect OS
# 2.
# 3. Download latest release
# 4. Set capabilities with sudo
# 5. Init config file
# 6. Display welcome message:
# - Edit config to configure your DB access and SSL certs
# - Add to PATH
# - How to launch CloudFire
os=`uname`
if [ ! $os = "Linux" ]; then
echo "${os} unsupported. Only Linux is supported."
exit -1
fi
# Exit if already installed
bin="$HOME/.cloudfire/bin/cloudfire"
if [ -f $bin ]; then
echo "${bin} exists. Aborting. If you'd like to upgrade your installation run\
cloudfire --upgrade"
exit 0
fi
echo 'Downloading the latest release...'
mkdir -p $HOME/.cloudfire/bin
curl https://github.com/CloudFire-LLC/cloudfire/releases/download/latest/cloudfire_amd64 > $HOME/.cloudfire/bin/cloudfire
echo 'Setting Linux capabilities on the binary... sudo is required'
sudo bash -c 'setcap "cap_net_admin,cap_net_raw,cap_dac_read_search" $HOME/.cloudfire/bin/cloudfire'
echo 'Initializing default configuration...'
mkdir -p $HOME/.cloudfire/ssl
hostname=$(hostname)
openssl req -new -x509 -sha256 -newkey rsa:2048 -nodes \
-keyout $HOME/.cloudfire/ssl/key.pem \
-out $HOME/.cloudfire/ssl/cert.pem \
-days 365 -subj "/CN=${hostname}"
chmod 0600 $HOME/.cloudfire/ssl/key.pem
chmod 0644 $HOME/.cloudfire/ssl/cert.pem
secret_key_base="$(openssl rand -base64 48)"
live_view_signing_salt="$(openssl rand -base64 24)"
db_key="$(openssl rand -base64 32)"
wg_server_key="$(wg genkey)"
echo "{
\"database_url\": \"ecto://postgres:postgres@127.0.0.1/cloudfire\",
\"secret_key_base\": \"${secret_key_base}\",
\"live_view_signing_salt\": \"${live_view_signing_salt}\",
\"db_key\": \"${db_key}\",
\"ssl_cert_file\": \"${$HOME}/.cloudfire/ssl/cert.pem\",
\"ssl_key_file\": \"${HOME}/.cloudfire/ssl/key.pem\",
\"url_host\": \"${hostname}\",
\"wg_server_key\": \"$(wg genkey)\"
}" > $HOME/.cloudfire/config.json
chmod 0600 $HOME/.cloudfire/config.json

View File

@@ -20,8 +20,8 @@ live_view_signing_salt="$(openssl rand -base64 24)"
secret_key_base="$(openssl rand -base64 48)"
db_user=cloudfire
# Base64 includes forward slashes which are problematic in the
# DB_URL connect string, so use hex.
# base64 includes forward slashes which are problematic in the
# db_url connect string, so use hex.
db_password="$(openssl rand -hex 16)"
db_key="$(openssl rand -base64 32)"