Secure the Unraid GUI behind Authentik
Assumptions
- You are using forward authentication behind a reverse proxy
- The internal host for Unraid does not use HTTPS
- The only user allowed for the Unraid web GUI is
root
- The password for Unraid is stored as a user attribute
- The attribute can come from a group to which the user belongs
Instructions
- In Authentik create a new Scope Mapping
- The Name can be whatever you would like
- The Scope name MUST be
ak_proxy
- Copy the contents of
unraid_scope_mapping.py
into the Expression field
- Update the “Important Variables” section of the Expression field
- Set
internal_host
to the domain or IP address of your Unraid instance- If the Unraid web GUI isn’t using the default HTTP port, make sure you specify it
- Set
external_host
to your Unraid domain name
- Set
- Create a new Provider of type Proxy Provider named “Unraid”
- Set it to “Forward auth (single application)”
- Fill in the External host field to match the external host specified above
- Under Advanced protocol settings > Additional scopes make sure that you select the scope that you created in the first step
- Create a new Application and attach the Provider to the Application
- Attach the new Application to the Outpost of your choice
- Follow the Authentik instructions for setting up your reverse proxy using forward authentication
unraid_scope_mapping.py
###############################################################################
##### Important Variables #####################################################
###############################################################################
# Set this to your internal Unraid domain or IP
# Include the port if necessary
internal_host = "unraid.internal"
# Set this to your external Unraid domain
external_host = "unraid.external"
# This is the attribute the password is stored in on the user
password_attribute = "unraid_password"
###############################################################################
import hashlib
# Check if the user has the password attribute
# My users inherit it from the group "Unraid Admins"
all_attributes = request.user.group_attributes()
if password_attribute not in all_attributes:
ak_logger.info(f"user does not have the {password_attribute} attribute")
return {}
# As far as I know, this is the only valid username for Unraid
unraid_username = "root"
# Get the Unraid password from the attributes
unraid_password = all_attributes[password_attribute]
# Build the URI for the internal Unraid login page
unraid_uri = f"http://{internal_host}/login"
# Login to Unraid
response = requests.post(
unraid_uri,
data={
"username": unraid_username,
"password": unraid_password,
},
allow_redirects=False, # It's important to not follow redirects
)
# Unraid returns a redirect to the main page when successful
if response.status_code != 302:
ak_logger.error(f"the request failed with: {response.text}")
return {}
# Look for the Unraid cookie
# The Unraid cookie format is "unraid_{md5(server_name)}" so we can't just copy
# it directly
for key, value in response.cookies.items():
if key.startswith("unraid_"):
ak_logger.info(f"found cookie '{key}'; creating new cookie for host '{external_host}'")
# The session for the browser won't match the internal site, so we have
# to create a new key for our cookie
host_hash = hashlib.md5(external_host.encode()).hexdigest()
cookie = f"unraid_{host_hash}={value}; path=/; HttpOnly; SameSite=Lax"
# Add a header to set this new cookie in the response to the browser
return {
"ak_proxy": {
"user_attributes": {
"additionalHeaders": {
"Set-Cookie": cookie,
}
}
}
}
# Something went wrong if the login succeeded but the cookie is missing
ak_logger.error("cannot get unraid token")
return {}