Introduction
My parents and I share a DS923+. We mainly use it for storing photos. To save energy, the NAS is stopped at night and restarted in the morning. Restarting the NAS means that you need to log in to the UI at least once every day.
Over the past two years, my parents forgot their passwords a couple of times. They can't reset the password on their own (that's a limitation with Synology's "local" accounts), and I need to log in and reset the password for them.
Challenges with Local Accounts and SSO
A couple of years ago, Synology introduced SSO (single sign-on). Since 7.2, DSM supports generic OIDC providers, and supports logging into local users (it used to be only possible for LDAP users).
Since my parents are always signed in into their Google account, I figured it would be possible to use OIDC with Google... Except it won't work with local accounts.
Here is what I tried: I created a My OIDC configuration for Google looked like this:
Then, I configured my Synology to use Google's OIDC endpoint:
The problem arose with the "Username claim". I want to log into my local account mael.valais
, but none of the claims in Google's ID tokens contain that username. Here is an example of a Google ID token:
{
"iss": "https://accounts.google.com",
"aud": "1234987819200.apps.googleusercontent.com",
"sub": "10769150350006150715113082367",
"email": "jsmith@example.com",
"email_verified": "true",
"iat": 1353601026,
"exp": 1353604926
}
Forking Dex to use it as an OIDC middleware for Google OIDC
I figured I could use Dex to act as a middleware between Synology's OIDC client and Google's OIDC server. My goal was to "augment" Google's JWTs with Synology's usernames by looking up the user by email.
Dex isn't as flexible as I would have hoped. To make it work, I had to fork it to change the internals of the Google OIDC connector.
Fork: https://github.com/maelvls/dex/tree/google-to-synology-sso
This fork is a fork of the fork presented in https://github.com/dexidp/dex/pull/2954. It builds on the idea of the ExtendPayload
interface, which I slightly adjusted to pass the original claims since I needed access to the email contained in the JWT provided by Google.
With this fork, you will need to set three more environment variables:
SYNO_PASSWD=redacted
SYNO_USER=mael.valais
SYNO_URL=http://127.0.0.1:5000
When the OIDC flow with Google is done and before Dex issues its own JWT, I added some code to add the claim username
. With this modified Dex, the JWT looks like this:
{
"at_hash": "-j6HZYvzDaqkQB2KxIgSyw",
"aud": "caddy",
"c_hash": "8SK3tobDYgaI3cnDzkmi5g",
"email": "mael65@gmail.com",
"email_verified": true,
"exp": 1713387587,
"iat": 1713301187,
"iss": "https://login.mysynodomain.dev/dex",
"name": "Maël Valais",
"nonce": "MFZFSkESL1XqdQmbvr0T43Kn7v0CzLap",
"sub": "ChUxMDAzNjk3OTQzNjg3MDAwOTk5MTISBmdvb2dsZQ",
"username": "mael.valais"
}
Here is the updated configuration in DSM:
Using the fork of Dex
First, install zig
and ko
. That will allow you to cross-compile Dex to linux/amd64
on macOS without Buildx (cross-compiling is required because Dex's sqlite library needs CGO)
brew install ko zig
Clone the fork:
git clone https://github.com/maelvls/dex --branch google-to-synology-sso
Then, build the image:
CC="zig cc -target x86_64-linux" CXX="zig c++ -target x86_64-linux" CGO_ENABLED=1 \
KO_DOCKER_REPO=ghcr.io/maelvls/dex \
KO_DEFAULTBASEIMAGE=alpine \
ko build ./cmd/dex --bare --tarball /tmp/out.tar --push=false
Then, copy the image to your NAS:
ssh yournas /usr/local/bin/docker load </tmp/out.tar
Then, create a file dex.yaml
on your NAS:
issuer: https://login.mysynodomain.dev/dex
storage:
type: sqlite3
config:
file: dex.sqlite
web:
http: 0.0.0.0:5556
logger:
level: debug
oauth2:
skipApprovalScreen: true
alwaysShowLoginScreen: false
staticClients:
- id: synology
name: 'Synology'
redirectURIs:
- 'https://mysynodomain.dev/'
secret: foo # Use openssl rand -hex 16 to generate this.
connectors:
- type: google
id: google
name: Google
config:
issuer: https://accounts.google.com
clientID: $GOOGLE_CLIENT_ID
clientSecret: $GOOGLE_CLIENT_SECRET
redirectURI: https://login.mysynodomain.dev/dex/callback
# I have disabled email login.
enablePasswordDB: false
Finally, run Dex:
docker run --name dex -d \
-v $HOME/dex.yaml:/dex.yaml \
-v $HOME/dex.sqlite:/dex.sqlite \
-e SYNO_PASSWD=redacted \
-e SYNO_USER=mael.valais \
-e SYNO_URL=http://127.0.0.1:5000 \
-e GOOGLE_CLIENT_ID=207842732284-l7nhetlsvimmds80fa2knir8fundp3h4.apps.googleusercontent.com \
-e GOOGLE_CLIENT_SECRET=redacted \
-p 5556:5556 \
ghcr.io/maelvls/dex serve /dex.yaml
Conclusion
With this method, my parents can log into the NAS with their Google account and no longer have to remember their Synology username and password.
Although it works, I wish I didn't have to fork Dex to customize the claims it puts into the JWT payload. I came across a couple of designs that would aim to make Dex more extendable, but none have been implemented yet.
Top comments (0)