Skip to content

Commit

Permalink
added the basic_auth stuff 🚀 (#7)
Browse files Browse the repository at this point in the history
Signed-off-by: Sam Dennon <[email protected]>
  • Loading branch information
SamEureka authored Jul 3, 2024
1 parent 2fb969c commit 6ecfeee
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 30 deletions.
30 changes: 24 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# <img src="caddy-dumb.png" alt="crappy caddy logo" width="60" /> caddy-reverso

__caddy-reverso__ is a Caddy based reverse-proxy server running on your Raspberry Pi! It implements ACME DNS Challenge to obtain tls certificates from Let's Encrypt and is configured via balena device variables in the cloud dashboard. Tested on all Raspberry Pi from v1 to Pi-4-64!
__caddy-reverso__ is a Caddy based reverse-proxy server running on your Raspberry Pi! It implements ACME DNS Challenge to obtain tls certificates from Let's Encrypt & basic authentication. Everything is configured via balena device variables in the balena.io cloud dashboard. Tested on all Raspberry Pi from v1 to Pi-4-64!

## Status
* Works!!! ~~I broke some stuff... working on it.~~
Expand All @@ -9,19 +9,37 @@ __caddy-reverso__ is a Caddy based reverse-proxy server running on your Raspberr
* Wildcard certificates are working!!
* ACME provisioning works (digital ocean tested sat)
* The python is pretty janky...
* Basic authentication (user/pass) works.

## TODO
1. Document
2. Figure out how to publish as an app on [Balena Hub](https://hub.balena.io)
1. Document properly
~~2. Figure out how to publish as an app on [Balena Hub](https://hub.balena.io)~~
3. Test other DNS providers for the ACME provisioning
~~4. OAUTH authentication integration ([module](https://github.com/greenpau/caddy-security))~~
~~5. Basic authentication integration (should be easy :laughing:)~~
4. Look into adding in oauth2-proxy to handle auth stuff instead of caddy modules. basicauth didn't work as expected and caddy-security is a config nightmare

## Bare Bones Instructions

### Balena-CLI method
> [!CAUTION]
> assumes you have some balena-cli chops and a decent understanding of what you are trying to do here. Reverse-proxy isn't beginner shit.
1. In the [docker-compose.yml](./docker-compose.yml) file set your DNS provider. [Quick Link: List of all supported Providers](https://github.com/orgs/caddy-dns/repositories?type=all)
2. Deploy to your Fleet using [`balena push`](https://docs.balena.io/learn/deploy/deployment/#balena-push) <-- click for balena-cli docs
3. Start the application you want to proxy. You'll need the IP and Port at a minimum.
4. Create a `HOST_<number>` (see the table below) device variable for your to-be-proxied application. This will be parsed by the caddy-config.py script so follow the format exactly.
5. See the Device Variables table below. You'll need `DNS_API_KEY`, `DNS_PROVIDER`, & at least one `HOST_<number>`
6. These instructions are terrible... if you need help, create an issue. I'll try to help but I'm not going to teach you networking or help you set up your DNS. I will try to help you configure caddy-reverso only.

> [!TIP]
> Example name: `HOST_1` value: `nodered|awesomedomain.com|192.168.0. 13|4200|true|true`
> ***This will proxy*** `https://nodered.awesomedomain.com` ***to*** `192.168.0.13:4200`
## Device Variables
|Name|Value|Notes|
|---|---|---|
|HOST_\<number>| \<host>\|\<domain>\|\<ip>\|\<port>\|\<wildcard (true or false)>|The name must start with `HOST_` and have a number. Example name: `HOST_13` The values must separated with the pipe symbol `\|`. Example value: `nodered\|awesomedomain.com\|192.168.0.13\|4200\|true` If you aren't sure why/if you need a wildcard... set it to `false`|
|HOST_\<number>| \<host>\|\<domain>\|\<ip>\|\<port>\|\<wildcard (true or false)>\|\<auth_req (true or false)>|The name must start with `HOST_` and have a number. Example name: `HOST_13` The values must separated with the pipe symbol `\|`. Example value: `nodered\|awesomedomain.com\|192.168.0.13\|4200\|true\|true` If you aren't sure why/if you need a wildcard... set it to `false`|
|BASIC_AUTH_USER|`your-username`|This is only required if 'auth_req' is set to true in any of your HOST_X variables. |
|BASIC_AUTH_PASSWORD|`your-password`|This is only required if 'auth_req' is set to true in any of your HOST_X variables. |
|DNS_PROVIDER|digitalocean, cloudflare, googleclouddns... etc|This is the value provided to the ACME DNS Challenge and is also used to build the DNS Provider module. It needs to be set in the [docker-compose.yml](./docker-compose.yml) and set as a device variable. Check the [DNS Provider module WIKI](https://caddy.community/t/how-to-use-dns-provider-modules-in-caddy-2/8148) for general information about how this works and to find out if your DNS provider is supported. [Quick Link: List of all Providers](https://github.com/orgs/caddy-dns/repositories?type=all)|
|DNS_API_KEY|\<string of randomness>|See your DNS provider's doccumentation on how to create an API Key.|
|DNS_EMAIL|[email protected]|This is OPTIONAL. This is the email address provided to the ACME DNS Challenge process. If you don't set an email variable you'll get a WARN in the logs but it will all still work.|
20 changes: 11 additions & 9 deletions balena.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
name: balenaCaddyReverser
name: caddy-reverso
joinable: false
type: sw.application
description: >-
balenaCaddyReverser is a Caddy based reverse-proxy server running on your Raspberry Pi! It implements ACME DNS Challenge to obtain tls certificates from Let's Encrypt and is configured via balena device variables in the cloud dashboard. Tested on all Raspberry Pi from v1 to Pi-4-64!
caddy-reverso is a Caddy based reverse-proxy server running on your Raspberry Pi! It implements ACME DNS Challenge to obtain tls certificates from Let's Encrypt & basic authentication. Everything is configured via balena device variables in the balena.io cloud dashboard. Tested on all Raspberry Pi from v1 to Pi-4-64!
post-provisioning: >-
### Using balenaCaddyReverser
### Using caddy-reverso
- Minimum device variables: DNS_PROVIDER, DNS_API_KEY, and a HOST_<number>
- Minimum device variables: DNS_PROVIDER, DNS_API_KEY, and a HOST_<number>
- See instructions at [balenaCaddyReverser](https://github.com/SamEureka/balenaCaddyReverser)
- See terrible instructions at [caddy-reverso](https://github.com/SamEureka/caddy-reverso)
assets:
repository:
type: blob.asset
data:
url: 'https://github.com/SamEureka/balenaCaddyReverser'
url: 'https://github.com/SamEureka/caddy-reverso'
logo:
type: blob.asset
data:
url: >-
https://raw.githubusercontent.com/SamEureka/balenaCaddyReverser/main/caddy-dumb.png
https://raw.githubusercontent.com/SamEureka/caddy-reverso/main/caddy-dumb.png
data:
defaultDeviceType: raspberrypi3-64
supportedDeviceTypes:
- rpi
- raspberry-pi
- raspberry-pi2
- raspberrypi3
- raspberrypi0-2w-64
- raspberrypi3-64
- raspberrypi400-64
- raspberrypi4-64
version: 2.5.2
version: 3.0.15
2 changes: 2 additions & 0 deletions caddy/Dockerfile.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## balenaCaddy/caddy/Dockerfile.template ##
## Sam Dennon//2022 ##
## Updated // 2024 ##


FROM balenalib/%%BALENA_ARCH%%-alpine-golang:1.18-3.16-build AS build

Expand Down
46 changes: 31 additions & 15 deletions caddy/caddy-config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
## Caddy Configurator
## Sam Dennon // 2022
## Updated // July 2024

import os
import subprocess
import re
import time
import textwrap

## Removes all the crazy indenting caused by the multi-line strings.
Expand All @@ -13,33 +13,40 @@
## Grab some of the device variables
DNS_RESOLVERS = os.environ['DNS_RESOLVERS'] if 'DNS_RESOLVERS' in os.environ else '1.1.1.1'
DNS_EMAIL = os.environ['DNS_EMAIL'] if 'DNS_EMAIL' in os.environ else ''
DNS_PROVIDER = os.environ[
'DNS_PROVIDER'] if 'DNS_PROVIDER' in os.environ else 'XXX'
DNS_API_KEY = os.environ[
'DNS_API_KEY'] if 'DNS_API_KEY' in os.environ else 'XXX'
DNS_PROVIDER = os.environ['DNS_PROVIDER'] if 'DNS_PROVIDER' in os.environ else 'XXX'
DNS_API_KEY = os.environ['DNS_API_KEY'] if 'DNS_API_KEY' in os.environ else 'XXX'
BASIC_AUTH_USER = os.environ['BASIC_AUTH_USER'] if 'BASIC_AUTH_USER' in os.environ else 'admin'
BASIC_AUTH_PASSWORD = os.environ['BASIC_AUTH_PASSWORD'] if 'BASIC_AUTH_PASSWORD' in os.environ else 'password'

## Generate hashed password using Caddy's built-in utility
def generate_hashed_password(password):
result = subprocess.run(['caddy', 'hash-password', '--plaintext', password], capture_output=True, text=True)
return result.stdout.strip()

hashed_password = generate_hashed_password(BASIC_AUTH_PASSWORD)

## Pulls all the HOSTs out of the device variables and puts them in a list of dicts.
## Format for device variable - Name: HOST_<number>, Value: <host>|<domain>|<ip>|<port>|<wildcard (true or false)>
## The name must start with 'HOST_' and have a number. The value must separated with the pipe symbol '|'
## Format for device variable - Name: HOST_<number>, Value: <host>|<domain>|<ip>|<port>|<wildcard (true or false)>|<auth_req (true or false)>
## The name must start with 'HOST_' and have a number. The value must be separated with the pipe symbol '|'
def create_env_list():
output = []
for key, val in os.environ.items():
if re.match("HOST_[0-9]", key):
host = val.split('|')
if len(host) == 5:
if len(host) == 6: # Expecting an additional auth_req flag
try:
output.append({
'host': host[0],
'domain': host[1],
'ip': host[2],
'port': host[3],
'wildcard': host[4]
'wildcard': host[4],
'auth_req': host[5] # Add auth_req flag
})
except IndexError:
print('''
FORMAT ERROR: Check HOST variable format.
EXPECTING: <host>|<domain>|<ip>|<port>|<wildcard (true or false)>
EXPECTING: <host>|<domain>|<ip>|<port>|<wildcard (true or false)>|<auth_req (true or false)>
''')
return output

Expand Down Expand Up @@ -83,23 +90,31 @@ def generate_matcher_options():
!! Create HOST_<number> device variable in balena.io console !!
!! Format for device variable - !!
!! Name: HOST_<number> !!
!! Value: <host>|<domain>|<ip>|<port>|<wildcard (true or false)> !!
!! Value: <host>|<domain>|<ip>|<port>|<wildcard (true or false)>|<auth_req (true or false)> !!
!! The name must start with 'HOST_' and have a number. !!
!! The value must separated with the pipe symbol '|' !!
!! The value must be separated with the pipe symbol '|' !!
"""
temp_list = []
for e in list:
basicauth_block = f"""
basicauth {{
{BASIC_AUTH_USER} {hashed_password}
}}
""" if e['auth_req'] == 'true' else ''

if (e['wildcard'] == 'true'):
temp_list.append(f"""
@{e['host']}wild host *.{e['host']}.{e['domain']}
handle @{e['host']}wild {{
reverse_proxy {e['ip']}:{e['port']}
{basicauth_block}
reverse_proxy {e['ip']}:{e['port']}
}}
""")
temp_list.append(f"""
@{e['host']} host {e['host']}.{e['domain']}
handle @{e['host']} {{
reverse_proxy {e['ip']}:{e['port']}
{basicauth_block}
reverse_proxy {e['ip']}:{e['port']}
}}
""")
return ''.join(temp_list)
Expand Down Expand Up @@ -135,5 +150,6 @@ def write_caddyfile():
if (create_env_list() != []):
os.system('caddy run --config /etc/caddy/Caddyfile --adapter caddyfile')


## Idle... I like balena-idle as a fallback for troubleshooting. You can comment this out if you like.
os.system('balena-idle')
os.system('balena-idle')

0 comments on commit 6ecfeee

Please sign in to comment.