Auth Callout - Decentralized in Authentication and Authorization
Auth callout is a new feature in NATS v2.10.0 providing an extension point for integrating with alternative identity and access management (IAM) backends. See the official docs for more details.
This example demonstrates how auth callout can be configured using decentralized auth. This is combined with a basic service that handles the authorization requests delegated by the NATS server.
$ nbe run auth/callout-decentralized/cliView the source code or learn how to run this example yourself
Code
#!/bin/bash
set -eou pipefail
NATS_URL="nats://localhost:4222"
Bootstrap the resolver
Create the operator, generate a signing key (which is a best practice), and initialize the default SYS account and sys user. Note: if this is an existing environment, this bootstrapping can be skipped.
nsc add operator --generate-signing-key --sys --name local
A follow-up edit of the operator enforces signing keys are used for
accounts as well. Setting the server URL is a convenience so that
it does not need to be specified with call nsc push
.
nsc edit operator \
--require-signing-keys \
--account-jwt-server-url "$NATS_URL"
This command generates the bit of configuration to be used by the server to setup the embedded JWT resolver.
nsc generate config \
--nats-resolver \
--sys-account SYS > resolver.conf
Create the most basic config that simply includes the generated resolver config.
cat <<- EOF > server.conf
include resolver.conf
EOF
Start the server.
nats-server -c server.conf > /dev/null 2>&1 &
sleep 1
Setup application accounts
Setup two application accounts for demonstration.
nsc add account APP1
nsc edit account APP1 --sk generate
nsc add account APP2
nsc edit account APP2 --sk generate
Push the two app accounts up to the server.
nsc push -A
Create a user per account.
nsc add user --account APP1 --name app1
nsc add user --account APP2 --name app2
Generate creds for the two app accounts to show that they work as expected without auth callout enabled.
nsc generate creds --account APP1 --name app1 > app1.creds
nsc generate creds --account APP2 --name app2 > app2.creds
nats --creds app1.creds pub test 'hello from app1'
nats --creds app2.creds pub test 'hello from app2'
Setup auth account for callout
Create an AUTH
account which will be registered as the
account for the auth callout service.
nsc add account AUTH
nsc edit account AUTH --sk generate
Create a user for the auth callout service. Extract the public key of the user so that it can be used when configuring auth callout on the account.
nsc add user --account AUTH --name auth
USER_PUB=$(nsc describe user --account AUTH --name auth --field sub | jq -r)
Generate an Xkey for auth callout.
nsc generate nkey --curve 2> auth.xk
XKEY_SEED=$(sed -n "1,1p" auth.xk)
XKEY_KEY=$(sed -n "2,1p" auth.xk)
APP1_PUB=$(nsc describe account APP1 --field sub | jq -r)
APP2_PUB=$(nsc describe account APP2 --field sub | jq -r)
Edit the AUTH account to allow it to be used by the auth callout service.
The --allowed-account
option is used to define which accounts this
account is allowed to bind authorized users to. In this case, *
indicates
that any account can be bound. However if there are select accounts, they
would be listed via their public nkey.
nsc edit authcallout \
--account AUTH \
--curve $XKEY_KEY \
--auth-user $USER_PUB \
--allowed-account '*'
Push the AUTH account up to the server.
nsc push -A
sleep 2
Confirm existing creds still work even with auth callout enabled.
nats --creds app1.creds pub test 'hello from app1'
nats --creds app2.creds pub test 'hello from app2'
Setup auth callout service
Next, we need the signing keys for the application accounts that the auth callout service is allowed to create and bind users to. First we extract the signing key for each account. (Helper function to copy the signing key.)
function extract_signing_key() {
sk=$(nsc describe account $1 --field 'nats.signing_keys[0]' | tr -d '"')
cat "/root/.local/share/nats/nsc/keys/keys/${sk:0:1}/${sk:1:2}/${sk}.nk"
}
extract_signing_key APP1 > APP1.nk
extract_signing_key APP2 > APP2.nk
We also need the signing key of the AUTH account itself to sign the responses.
extract_signing_key AUTH > AUTH.nk
In order for the auth callout service to be able to connect, we need
the credentials for the auth
user.
nsc generate creds --account AUTH --name auth > auth.creds
Write out a couple users emulating a user directory backend.
cat <<- EOF > users.json
{
"alice": {
"pass": "alice",
"account": "APP1"
},
"bob": {
"pass": "bob",
"account": "APP2",
"permissions": {
"pub": {
"allow": ["bob.>"]
},
"sub": {
"allow": ["bob.>"]
},
"resp": {
"max": 1
}
}
}
}
EOF
Start the auth callout service passing the creds, account signing keys, as well as the Xkey seed that was generated earlier.
echo 'Starting auth callout service...'
service \
-nats.creds=auth.creds \
-issuer.seed=AUTH.nk \
-xkey.seed=$XKEY_SEED \
-signing.keys=$APP1_PUB:APP1.nk,$APP2_PUB:APP2.nk \
-users=users.json &
sleep 2
The final requirement for clients to be able to connect is having a set of credentials of the AUTH acount which will be used to by the server to delegate to the correct auth callout service. Add a sentinel user for the AUTH account that is required to be passed along with additional credentials.
nsc add user --account AUTH --name sentinel --deny-pubsub ">"
nsc generate creds --account AUTH --name sentinel > sentinel.creds
echo 'Client request from alice...'
client \
-creds=sentinel.creds \
-user alice \
-pass alice
echo 'Client request from bob...'
client \
-creds=sentinel.creds \
-user bob \
-pass bob
Output
[ OK ] generated and stored operator key "OAVKZTG7ST62YCPUJYJ6LBQDMKLAUD4NRBPZ4GXUXNUJW2ATCFKKYGHV" [ OK ] added operator "local" [ OK ] When running your own nats-server, make sure they run at least version 2.2.0 [ OK ] created operator signing key: OB6J6M7MHZGVNMGU5HW4DTAXYEQSDUTUJ66IT3WC45PLFSI5BRGG3SY6 [ OK ] created system_account: name:SYS id:ADCYIGJX26H2UBH3MHHYQXNFBXDC7M566RQ2TWR7S6MV6RKMFAOLREV3 [ OK ] created system account user: name:sys id:UD3BM3IUIHJ2KPWLVNIMKBRPETZODNSGKW3LREBJ5BIZMQ45WQLIMY6O [ OK ] system account user creds file stored in `~/.local/share/nats/nsc/keys/creds/local/SYS/sys.creds` [ OK ] strict signing key usage set to: true [ OK ] set account jwt server url to "nats://localhost:4222" [ OK ] edited operator "local" [ OK ] generated and stored account key "AACGHWR4ZJADWDP5R2WWSMCJCVW2U6C2EI46YE3ZUG5BS3LSGFOHUXKQ" [ OK ] added account "APP1" [ OK ] added signing key "AARAUQKKV4YR6Z7ZUVVAQ4556N3VUELZJYOK2AUUIAOTRKLFH6DGUSOB" [ OK ] edited account "APP1" [ OK ] generated and stored account key "ADW7J6ARMFQWXJLXVIKERQG6VU6VPANSPTCWS6OD25HCUGBYMO3JNOI6" [ OK ] added account "APP2" [ OK ] added signing key "AAFUFMWWJIT7MXQY2AJQ26Z2ER7ATUSXI4DRGAFXJNZ34DRWN4ZOQD6L" [ OK ] edited account "APP2" [ OK ] push to nats-server "nats://localhost:4222" using system account "SYS": [ OK ] push APP1 to nats-server with nats account resolver: [ OK ] pushed "APP1" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] push APP2 to nats-server with nats account resolver: [ OK ] pushed "APP2" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] push SYS to nats-server with nats account resolver: [ OK ] pushed "SYS" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] generated and stored user key "UCNHFM2H35DES7UXTWMR2RW7CETNPFEZRUGNH2IALANAER7NRXZZHYXP" [ OK ] generated user creds file `~/.local/share/nats/nsc/keys/creds/local/APP1/app1.creds` [ OK ] added user "app1" to account "APP1" [ OK ] generated and stored user key "UCLWPUUA2BTUVVM7XBUDJQZA4G52O7XJBZODPHLOVFQ6IXI6CYM2XJCN" [ OK ] generated user creds file `~/.local/share/nats/nsc/keys/creds/local/APP2/app2.creds` [ OK ] added user "app2" to account "APP2" 19:30:39 Published 15 bytes to "test" 19:30:39 Published 15 bytes to "test" [ OK ] generated and stored account key "ADGHPZKVTHHU4E6WKC2SXVI7JUEHDOQHTRG3VEKPVSONYCCZ5SWIEQ47" [ OK ] added account "AUTH" [ OK ] added signing key "ADKBJ7UDVMVTOPED7IXNYMTD6IASSD5UKSGDHYDBCSYBWIFRXG6OUBJ6" [ OK ] edited account "AUTH" [ OK ] generated and stored user key "UDZANJLUPZQBIHY6OBC7BDPDQLGGPBA2NFWCD5D4UHOLPRPWDV52CXHN" [ OK ] generated user creds file `~/.local/share/nats/nsc/keys/creds/local/AUTH/auth.creds` [ OK ] added user "auth" to account "AUTH" [ OK ] added user "UDZANJLUPZQBIHY6OBC7BDPDQLGGPBA2NFWCD5D4UHOLPRPWDV52CXHN" [ OK ] added account "*" [ OK ] set curve key XC6EEPFGEHLZZ2Q45G3XYUD7FBU5WTYQLU4BOUO2AYM2MDMUAD3RXL2C [ OK ] push to nats-server "nats://localhost:4222" using system account "SYS": [ OK ] push APP1 to nats-server with nats account resolver: [ OK ] pushed "APP1" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] push APP2 to nats-server with nats account resolver: [ OK ] pushed "APP2" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] push AUTH to nats-server with nats account resolver: [ OK ] pushed "AUTH" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server [ OK ] push SYS to nats-server with nats account resolver: [ OK ] pushed "SYS" to nats-server NCQCZSICC22MKDYHNEDDISR6Q6YHZ23SQSSLO2RAUJ2XCTRKPKSXKTPX: jwt updated [ OK ] pushed to a total of 1 nats-server 19:30:46 Published 15 bytes to "test" 19:30:46 Published 15 bytes to "test" Starting auth callout service... [ OK ] added deny pub ">" [ OK ] added deny sub ">" [ OK ] generated and stored user key "UCHW2DVE6TZ3XYBU435BEGLVLNGVMZGSXEARFXS7GAZTKDN6MO53OC7E" [ OK ] generated user creds file `~/.local/share/nats/nsc/keys/creds/local/AUTH/sentinel.creds` [ OK ] added user "sentinel" to account "AUTH" Client request from alice... Client request from bob... 2023/10/23 19:30:48 responding to authorization request 2023/10/23 19:30:48 alice connected to nats://127.0.0.1:4222 2023/10/23 19:30:48 responding to authorization request 2023/10/23 19:30:48 bob connected to nats://127.0.0.1:4222