Important
Shipyard's built-in auth is not indented to protect a publicly hosted instance against unauthorized access. Instead you should use an auth provider compatible with your reverse proxy, or access Shipyard via your VPN, or implement your own SSO logic.
In cases where Shipyard is only accessibly within your home network, and you just want to add a login page, then the built-in auth may be sufficient, but keep in mind that configuration can still be accessed.
Shipyard has a basic login page included, and frontend authentication. You can enable this by adding users to the auth
section under appConfig
in your conf.yml
. If this section is not specified, then no authentication will be required to access the app, and the homepage will resolve to your dashboard.
Note
Since the auth is initiated in the main app entry point (for security), a rebuild is required to apply changes to the auth configuration.
You can trigger a rebuild through the UI, under Config --> Rebuild, or by running yarn build
in the root directory.
The auth
property takes an array of users. Each user needs to include a username, hash and optional user type (admin
or normal
). The hash property is a SHA-256 Hash of your desired password.
For example:
appConfig:
auth:
users:
- user: khulnasoft
hash: 4D1E58C90B3B94BCAD9848ECCACD6D2A8C9FBC5CA913304BBA5CDEAB36FEEFA3
type: admin
- user: bob
hash: 5E884898DA28047151D0E56F8DC6292773603D0D6AABBDD62A11EF721D1542D8
Shipyard uses SHA-256 Hash, a 64-character string, which you can generate using an online tool, such as this one or CyberChef (which can be self-hosted/ ran locally).
A hash is a one-way cryptographic function, meaning that it is easy to generate a hash for a given password, but very hard to determine the original password for a given hash. This means, that so long as your password is long, strong and unique, it is safe to store its hash in the clear. Having said that, you should never reuse passwords, hashes can be cracked by iterating over known password lists, generating a hash of each.
Once authentication is enabled, so long as there is no valid token in cookie storage, the application will redirect the user to the login page. When the user enters credentials in the login page, they will be checked, and if valid, then a token will be generated, and they can be redirected to the home page. If credentials are invalid, then an error message will be shown, and they will remain on the login page. Once in the application, to log out: the user can click the logout button (in the top-right), which will clear cookie storage, causing them to be redirected back to the login page.
With authentication set up, by default no access is allowed to your dashboard without first logging in with valid credentials. Guest mode can be enabled to allow for read-only access to a secured dashboard by any user, without the need to log in. A guest user cannot write any changes to the config file, but can apply modifications locally (stored in their browser). You can enable guest access, by setting appConfig.auth.enableGuestAccess: true
.
You can use the following properties to make certain pages, sections or items only visible to some users, or hide pages, sections and items from guests.
hideForUsers
- Page, Section or Item will be visible to all users, except for those specified in this listshowForUsers
- Page, Section or Item will be hidden from all users, except for those specified in this listhideForGuests
- Page, Section or Item will be visible for logged in users, but not for guests
For Example:
pages:
- name: Home Lab
path: home-lab.yml
displayData:
showForUsers: [admin]
- name: Intranet
path: intranet.yml
displayData:
hideForGuests: true
hideForUsers: [khulnasoft, bob]
- name: Code Analysis & Monitoring
icon: fas fa-code
displayData:
cols: 2
hideForUsers: [khulnasoft, bob]
items:
...
- name: Deployment Pipelines
icon: fas fa-rocket
displayData:
hideForGuests: true
items:
- title: Hide Me
displayData:
hideForUsers: [khulnasoft, bob]
Any user who is not an admin (with type: admin
) will not be able to write changes to disk.
You can also prevent any user from writing changes to disk, using preventWriteToDisk
. Or prevent any changes from being saved locally in browser storage, using preventLocalSave
. Both properties can be found under appConfig
.
To disable all UI config features, including View Config, set disableConfiguration
. Alternatively you can disable UI config features for all non admin users by setting disableConfigurationForNonAdmin
to true.
If you don't want to hash your password, you can instead leave out the hash
attribute, and replace it with password
which should have the value of an environmental variable name you wish to use.
Note that env var must begin with VUE_APP_
, and you must set this variable before building the app.
For example:
auth:
users:
- user: bob
password: VUE_APP_BOB
Just be sure to set VUE_APP_BOB='my super secret password'
before build-time.
If you'd also like to prevent direct visit access to your configuration file, you can set the ENABLE_HTTP_AUTH
environmental variable.
With basic auth, all logic is happening on the client-side, which could mean a skilled user could manipulate the code to view parts of your configuration, including the hash. If the SHA-256 hash is of a common password, it may be possible to determine it, using a lookup table, in order to find the original password. Which can be used to manually generate the auth token, that can then be inserted into session storage, to become a valid logged in user. Therefore, you should always use a long, strong and unique password, and if you instance contains security-critical info and/ or is exposed directly to the internet, and alternative authentication method may be better. The purpose of the login page is merely to prevent immediate unauthorized access to your homepage.
If you'd like to protect all your config files from direct access, you can set the BASIC_AUTH_USERNAME
and BASIC_AUTH_PASSWORD
environmental variables. You'll then be prompted to enter these credentials when visiting Shipyard.
Then, if you'd like your frontend to automatically log you in, without prompting you for credentials (insecure, so only use on a trusted environment), then also specify VUE_APP_BASIC_AUTH_USERNAME
and VUE_APP_BASIC_AUTH_PASSWORD
. This is useful for when you're hosting Shipyard on a private server, and just want to use auth for user management and to prevent direct access to your config files, while still allowing the frontend to access them. Note that a rebuild is required for these changes to take effect.
Shipyard also supports using a Keycloak authentication server. The setup for this is a bit more involved, but it gives you greater security overall, useful for if your instance is exposed to the internet.
Keycloak is a Java-based open source, high-performance, secure authentication system, supported by RedHat. It is easy to setup (with Docker), and enables you to secure multiple self-hosted applications with single-sign-on using standard protocols (OpenID Connect, OAuth 2.0, SAML 2.0 and social login). It's also very customizable, you can write or use custom themes, plugins, password policies and more. The following guide will walk you through setting up Keycloak with Shipyard. If you already have a Keycloak instance configured, then skip to Step 3.
First thing to do is to spin up a new instance of Keycloak. You will need Docker installed, and can then choose a tag, and pull the container from quay.io/keycloak/keycloak
Use the following run command, replacing the attributes (default credentials, port and name), or incorporate this into your docker-compose file.
docker run -d \
-p 8081:8080 \
--name auth-server \
-e KEYCLOAK_USER=admin \
-e KEYCLOAK_PASSWORD=admin \
quay.io/keycloak/keycloak:15.0.2
If you need to pull from DockerHub, a non-official image is available here. Or if you would prefer not to use Docker, you can also directly install Keycloak from source, following this guide.
You should now be able to access the Keycloak web interface, using the port specified above (e.g. http://127.0.0.1:8081
), login with the default credentials, and when prompted create a new password.
Before we can use Keycloak, we must first set it up with some users. Keycloak uses Realms (similar to tenants) to create isolated groups of users. You must create a Realm before you will be able to add your first user.
- Head over to the admin console
- In the top-left corner there is a dropdown called 'Master', hover over it and then click 'Add Realm'
- Give your realm a name, and hit 'Create'
You can now create your first user.
- In the left-hand menu, click 'Users', then 'Add User'
- Fill in the form, including username and hit 'Save'
- Under the 'Credentials' tab, give the new user an initial password. They will be prompted to change this after first login
The last thing we need to do in the Keycloak admin console is to create a new client
- Within your new realm, navigate to 'Clients' on the left-hand side, then click 'Create' in the top-right
- Choose a 'Client ID', set 'Client Protocol' to 'openid-connect', and for 'Valid Redirect URIs' put a URL pattern to where you're hosting Shipyard (if you're just testing locally, then * is fine), and do the same for the 'Web Origins' field
- Make note of your client-id, and click 'Save'
Now that your Keycloak instance is up and running, all that's left to do is to configure Shipyard to use it. Under appConfig
, set auth.enableKeycloak: true
, then fill in the details in auth.keycloak
, including: serverUrl
- the URL where your Keycloak instance is hosted, realm
- the name you gave your Realm, and clientId
- the Client ID you chose.
For example:
appConfig:
...
auth:
enableKeycloak: true
keycloak:
serverUrl: 'http://localhost:8081'
realm: 'khulnasoft-homelab'
clientId: 'shipyard'
Note that if you are using Keycloak V 17 or older, you will also need to set legacySupport: true
(also under appConfig.auth.keycloak
). This is because the API endpoint was updated in later versions.
If you use Keycloak with an external Identity Provier, you can set the idpHint: 'alias-of-kc-idp'
option to allow the IdP Hint to be passed to Keycloak. This will cause Keycloak to skip its login page and redirect the user directly to the specified IdP's login page. Set to the value of the 'Alias' field of the desired IdP as defined in Keycloak under 'Identity Providers'.
Keycloak allows you to assign users roles and groups. You can use these values to configure who can access various sections or items in Shipyard.
Keycloak server administration and configuration is a deep topic; please refer to the server admin guide to see details about creating and assigning roles and groups.
Once you have groups or roles assigned to users you can configure access under each section or item displayData.showForKeycloakUser
and displayData.hideForKeycloakUser
.
Both show and hide configurations accept a list of groups
and roles
that limit access. If a users data matches one or more items in these lists they will be allowed or excluded as defined.
sections:
- name: DeveloperResources
displayData:
showForKeycloakUsers:
roles: ['canViewDevResources']
hideForKeycloakUsers:
groups: ['ProductTeam']
items:
- title: Not Visible for developers
displayData:
hideForKeycloakUsers:
groups: ['DevelopmentTeam']
Depending on how you're hosting Shipyard and Keycloak, you may also need to set some HTTP headers, to prevent a CORS error. This would typically be the Access-Control-Allow-Origin [URL-of Shipyard]
on your Keycloak instance. See the Setting Headers guide in the management docs for more info.
Your app is now secured :) When you load Shipyard, it will redirect to your Keycloak login page, and any user without valid credentials will be prevented from accessing your dashboard.
From within the Keycloak console, you can then configure things like time-outs, password policies, etc. You can also backup your full Keycloak config, and it is recommended to do this, along with your Shipyard config. You can spin up both Shipyard and Keycloak simultaneously and restore both applications configs using a docker-compose.yml
file, and this is recommended.
If you encounter issues with your Keycloak setup, follow these steps to troubleshoot and resolve common problems.
-
Client Authentication Issue Problem: Redirect loop, if client authentication is enabled. Solution: Switch off "client authentication" in "TC clients" -> "Advanced" settings.
-
Double URL Problem: If you get redirected to "https://shipyard.my.domain/#iss=https://keycloak.my.domain/realms/my-realm" Solution: Make sure to turn on "Exclude Issuer From Authentication Response" in "TC clients" -> "Advanced" -> "OpenID Connect Compatibility Modes"
-
Problems with mutiple Shipyard Pages Problem: Refreshing or logging out of shipyard results in an "invalid_redirect_uri" error. Solution: In "TC clients" -> "Access settings" -> "Root URL" https://shipyard.my.domain/, valid redirect URIs must be /*
Shipyard also supports using a general OIDC compatible authentication server. In order to use it, the authentication section needs to be configured:
appConfig:
auth:
enableOidc: true
oidc:
clientId: [registered client id]
endpoint: [OIDC endpoint]
scope: [The scope(s) to request from the OIDC provider]
Because Shipyard is a SPA, a public client registration with PKCE is needed.
An example for Authelia is shared below, but other OIDC systems can be used:
identity_providers:
oidc:
clients:
- client_id: shipyard
client_name: shipyard
public: true
authorization_policy: 'one_factor'
require_pkce: true
pkce_challenge_method: 'S256'
redirect_uris:
- https://shipyard.local # should point to your shipyard endpoint
grant_types:
- authorization_code
scopes:
- 'openid'
- 'profile'
- 'roles'
- 'email'
- 'groups'
Groups and roles will be populated and available for controlling display similar to Keycloak above.
This documentation is specific to authentik
, however it may be useful in getting other idP's working with Shipyard
.
This guide will only walk through the following:
- Creating and configuring an OIDC provider
- Creating and configuring an application
- Assigning groups
- Configuring
Shipyard
to use the OIDC client - Show quick examples of how to hide/show
pages
,items
, andsections
using OIDC groups
This guide assumes the following:
- You have a working instance of
authentik
terminated with SSL - You have a working instance of
Shipyard
terminated with SSL - Users and groups are provisioned
- You are familiar with how
authentik
works in case you need to do further troubleshooting that is outside the scope of this guide.
Tip
It it recommended that you create groups specific for Shipyard
. Groups will allow you to display content based on group membership as well as limiting user access to Shipyard
. If you do not need this functionality, then you can forgo creating specific groups.
Tip
You can use the application wizard to create the provider and application at one time. This is the recommended route, but only the manual process will be outlined in this guide.
Login to the admin console for authentik
. Go to Applications
> Providers
. Click Create
.
A dialog box will pop-up, select the OAuth2/OpenID Provider
. Click Next
.
On the next page of the wizard, set the Name
, Authentication flow
, and Authorization flow
. See example below. Using the default-provider-authorization-implicit-consent
authorization flow on internal services and default-provider-authorization-explicit-consent
on external services is a common practice. However, it is fully up to you on how you would like to configure this option. Implicit
will login directly without user consent, explicit
will ask if the user approves the service being logged into with their user credentials.
Scroll down and configure the Protocol settings
. Set the Client type
to Public
. Add the Redirect URIs/Origins (RegEx)
. If the site is hosted at shipyard.lan.domain.com
, then you would enter as the example below.
Note
If you have an internal and external domain for Shipyard
, enter both URI's. Enter each URI on a new line.
Scroll down to set the Signing Key
. It is recommended to use the built in authentik Self-signed Certificate
here unless you have special needs for your own custom cert.
Expand Advanced protocol settings
then verify the Scopes
are set to what is highlighted in white
below. Set the Subject mode
to Based on the Users's Email
.
Lastly, toggle Include claims in id_token
to on. Click Finish
to complete creating the provider.
Grab the generated Client ID
and OpenID Configuration Issuer
URL by clicking the newly created provider as this will use this later when Shipyard
is configured to use the OIDC auth mechanism. In this tutorial, what was generated is used below. Obviously adjust the Client ID
that was generated and use your domain here for the issuer
.
Client ID: pzN9DCMLqHTTatgtYFg50cl0jn1NmCyBC3wreX15
OpenID Configuration Issuer: https://auth.domain.com/application/o/shipyard/
Make sure you are still in the authentik
admin console then go to Applications
> Applications
. Click Create
.
Next, it is required to give a user facing Name
, Slug
and assign the newly created provider. Use the example below if you have been following the guide. If you have used your own naming, then adjust accordingly. Click Create
once you are done.
Tip
Open the application in a new tab from the authentik
user portal and upload a custom icon. You can also enter a user facing Description
that the user would see.
If you would like to deny Shipyard
access from specific users who are not within authentik
based groups, you bind them to the application you just created now. authentik
will deny access to those who are not members of this group or groups. If you want to allow everyone access from your authentik
instance, skip this step.
Make sure you are still in the authentik
admin console then go to Applications
> Applications
. Click the newly created Shipyard
application.
Click the Policy/Group/User Bindings
tab at the top, then click Bind existing policy
. This assumes you have already created the groups you want to use for Shipyard
and populated users in those groups.
Click Group
for the binding type. Under Group
select the appropriate group you would like to bind. Make sure Enabled
is toggeled on. Click Create
.
Shipyard
will now be scoped only to users within the assigned groups you have bound the application to. Keep adding groups if you would like to adjust the dashboard visibilty based on group membership.
Important
It is highly recommended to edit your conf.yml
directly for this step.
Caution
Do not make the same mistake many have made here by including the fully qualified address for the OpenID Configuration URL
. Shipyard
will append the .well-known
configuration automatically. If the .well-known
URI is included the app will get redirect loops and 400
errors.
Enter the Client ID
in the clientId
field and OpenID Configuration Issuer
in the endpoint
field.
Below is how to configure the auth
section in the yaml syntax. Once this is enabled, when an attempt to access Shipyard
is made it will now redirect you to the authentik
login page moving forward.
appConfig:
theme: glass
layout: auto
iconSize: medium
auth:
enableOidc: true
oidc:
clientId: pzN9DCMLqHTTatgtYFg50cl0jn1NmCyBC3wreX15
endpoint: https://auth.domain.com/application/o/shipyard/
Using the hideForKeycloakUsers
configuration option is needed to use the authentik
groups that were created previously.
Adjusting pages
visibility:
pages:
- name: App Management
path: appmgmt.yml
displayData:
hideForKeycloakUsers:
groups:
- Shipyard Users
- name: Network Management
path: network.yml
displayData:
hideForKeycloakUsers:
groups:
- Shipyard Users
Adjusting items
visibility:
items:
- title: Authentik Admin
icon: authentik.svg
url: https://auth.domain.com/if/admin/
target: newtab
id: 0_1472_authentikadmin
displayData:
hideForKeycloakUsers:
groups:
- Shipyard Users
- title: Authentik User
icon: authentik-light.png
url: https://auth.domain.com/if/user/
target: newtab
id: 1_1472_authentikuser
Adjusting sections
visibility:
sections:
- name: Authentication
displayData:
sortBy: default
rows: 2
cols: 1
collapsed: false
hideForGuests: false
hideForKeycloakUsers:
groups:
- Shipyard Users
If you are self-hosting Shipyard, and require secure authentication to prevent unauthorized access, then you can either use Keycloak, or one of the following options:
- Authentication Server - Put Shipyard behind a self-hosted auth server
- VPN - Use a VPN to tunnel into the network where Shipyard is running
- IP-Based Access - Disallow access from all IP addresses, except your own
- Web Server Authentication - Enable user control within your web server or proxy
- OAuth Services - Implement a user management system using a cloud provider
- Password Protection (for cloud providers) - Enable password-protection on your site
Authelia is an open-source full-featured authentication server, which can be self-hosted and either on bare metal, in a Docker container or in a Kubernetes cluster. It allows for fine-grained access control rules based on IP, path, users etc, and supports 2FA, simple password access or bypass policies for your domains.
git clone https://github.com/authelia/authelia.git
cd authelia/examples/compose/lite
- Modify the
users_database.yml
the default username and password is authelia - Modify the
configuration.yml
anddocker-compose.yml
with your respective domains and secrets docker-compose up -d
For more information, see the Authelia docs
A catch-all solution to accessing services running from your home network remotely is to use a VPN. It means you do not need to worry about implementing complex authentication rules, or trusting the login implementation of individual applications. However it can be inconvenient to use on a day-to-day basis, and some public and corporate WiFi block VPN connections. Two popular VPN protocols are OpenVPN and WireGuard
If you have a static IP or use a VPN to access your running services, then you can use conditional access to block access to Shipyard from everyone except users of your pre-defined IP address. This feature is offered by most cloud providers, and supported by most web servers.
In Apache, this is configured in your .htaccess
file in Shipyard's root folder, and should look something like:
Order Deny,Allow
Deny from all
Allow from [your-ip]
In NGINX you can specify control access rules for a given site in your nginx.conf
or hosts file. For example:
server {
listen 8080;
server_name www.shipyard.example.com;
location / {
root /path/to/shipyard/;
passenger_enabled on;
allow [your-ip];
deny all;
}
}
In Caddy, Request Matchers can be used to filter requests
shipyard.site {
@public_networks not remote_ip [your-ip]
respond @public_networks "Access denied" 403
}
Most web servers make password protecting certain apps very easy. Note that you should also set up HTTPS and have a valid certificate in order for this to be secure.
First crate a .htaccess
file in Shipyard's route directory. Specify the auth type and path to where you want to store the password file (usually the same folder). For example:
AuthType Basic
AuthName "Please Sign into Shipyard"
AuthUserFile /path/shipyard/.htpasswd
require valid-user
Then create a .htpasswd
file in the same directory. List users and their hashed passwords here, with one user on each line, and a colon between username and password (e.g. [username]:[hashed-password]
). You will need to generate an MD5 hash of your desired password, this can be done with an online tool. Your file will look something like:
khulnasoft:$apr1$jv0spemw$RzOX5/GgY69JMkgV6u16l0
NGINX has an authentication module which can be used to add passwords to given sites, and is fairly simple to set up. Similar to above, you will need to create a .htpasswd
file. Then just enable auth and specify the path to that file, for example:
location / {
auth_basic "closed site";
auth_basic_user_file conf/htpasswd;
}
Caddy has a basic-auth directive, where you specify a username and hash. The password hash needs to be base-64 encoded, the caddy hash-password
command can help with this. For example:
basicauth /secret/* {
khulnasoft JDJhJDEwJEVCNmdaNEg2Ti5iejRMYkF3MFZhZ3VtV3E1SzBWZEZ5Q3VWc0tzOEJwZE9TaFlZdEVkZDhX
}
For more info about implementing a single sign on for all your apps with Caddy, see this tutorial
You can use the mod_auth module to secure your site with Lighttpd. Like with Apache, you need to first create a password file listing your usernames and hashed passwords, but in Lighttpd, it's usually called .lighttpdpassword
.
Then in your lighttpd.conf
file (usually in the /etc/lighttpd/
directory), load in the mod_auth module, and configure it's directives. For example:
server.modules += ( "mod_auth" )
auth.debug = 2
auth.backend = "plain"
auth.backend.plain.userfile = "/home/lighttpd/.lighttpdpassword"
$HTTP["host"] == "shipyard.my-domain.net" {
server.document-root = "/home/lighttpd/shipyard.my-domain.net/http"
server.errorlog = "/var/log/lighttpd/shipyard.my-domain.net/error.log"
accesslog.filename = "/var/log/lighttpd/shipyard.my-domain.net/access.log"
auth.require = (
"/docs/" => (
"method" => "basic",
"realm" => "Password protected area",
"require" => "user=khulnasoft"
)
)
}
Restart your web server for changes to take effect.
There are also authentication services, such as Ory.sh, Okta, Auth0, Firebase. Implementing one of these solutions would involve some changes to the Auth.js
file, but should be fairly straightforward.
If you are hosting Shipyard on a cloud platform, you will probably find that it has built-in support for password protected access to web apps. For more info, see the relevant docs for your provider, for example: Netlify Password Protection, Cloudflare Access, AWS Cognito, Azure Authentication and Vercel Password Protection.