From 4bec2aa5e0918fe27bc2ad923594d8fdc409ccb9 Mon Sep 17 00:00:00 2001 From: Jamil Bou Kheir Date: Tue, 29 Jun 2021 21:08:04 -0700 Subject: [PATCH] install.sh and refactored config check in --- .ci/functional_test.sh | 10 ++-- .tool-versions | 2 +- README.md | 61 ++++++++++++------------ apps/cf_common/lib/config_file.ex | 73 +++++++++++++++++++++++++++++ apps/cf_common/mix.exs | 1 + apps/cf_http/assets/static/logo.svg | 22 +++++---- config/releases.exs | 40 +++++++++------- install.sh | 57 ++++++++++++++++++++++ pkg/ubuntu-20.04/DEBIAN/postinst | 4 +- 9 files changed, 205 insertions(+), 65 deletions(-) create mode 100644 apps/cf_common/lib/config_file.ex create mode 100755 install.sh diff --git a/.ci/functional_test.sh b/.ci/functional_test.sh index 0f7379fc1..0f38e862f 100755 --- a/.ci/functional_test.sh +++ b/.ci/functional_test.sh @@ -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/ diff --git a/.tool-versions b/.tool-versions index 9c8ff007c..3c8e6982c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ -erlang 24.0.2 +erlang 24.0.3 elixir 1.12.1-otp-24 nodejs lts diff --git a/README.md b/README.md index 66a29d205..92243e599 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/apps/cf_common/lib/config_file.ex b/apps/cf_common/lib/config_file.ex new file mode 100644 index 000000000..1f55807dd --- /dev/null +++ b/apps/cf_common/lib/config_file.ex @@ -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 diff --git a/apps/cf_common/mix.exs b/apps/cf_common/mix.exs index 202fa08ba..83555b903 100644 --- a/apps/cf_common/mix.exs +++ b/apps/cf_common/mix.exs @@ -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} diff --git a/apps/cf_http/assets/static/logo.svg b/apps/cf_http/assets/static/logo.svg index 6bafb3852..bc06cd0ed 100644 --- a/apps/cf_http/assets/static/logo.svg +++ b/apps/cf_http/assets/static/logo.svg @@ -1,17 +1,19 @@ - - CloudFire logo - + + + Slice + Created with Sketch. - - - - cloud - - - ire + + + CloudFire + diff --git a/config/releases.exs b/config/releases.exs index 85ad8a44b..d7e2d0065 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -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. diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..9f08ecb98 --- /dev/null +++ b/install.sh @@ -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 diff --git a/pkg/ubuntu-20.04/DEBIAN/postinst b/pkg/ubuntu-20.04/DEBIAN/postinst index 75022873a..1550a4ebe 100755 --- a/pkg/ubuntu-20.04/DEBIAN/postinst +++ b/pkg/ubuntu-20.04/DEBIAN/postinst @@ -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)"