Deployment guide

Casdoor 4.24.0

Overview

This guide covers launching and using the Casdoor 4.24.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud identity, secrets and PKI listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Casdoor is an open-source identity and access management (IAM) and single sign-on server with a web console. Your applications sign people in through it with OpenID Connect / OAuth 2.0, SAML 2.0 or CAS.

Casdoor manages organisations, users, groups, roles and permissions. It supports passwords, one-time codes, WebAuthn passkeys and multi-factor authentication, and it can federate sign-in to other identity providers.

The SecureAnyCloud image is a working identity server out of the box. What you get:

  • nginx in front, Casdoor behind it. nginx is the only public listener (ports 80 and 443). Casdoor listens on 127.0.0.1:8000, the loopback address only, and keeps its data in an embedded SQLite database in /var/lib/casdoor (no separate database server).
  • HTTPS from the first boot. nginx serves the console and every protocol endpoint with a certificate generated on your instance's first boot (an end-entity certificate, not a certificate authority), and port 80 redirects to HTTPS. Casdoor's OIDC issuer is https://<your instance's name>.
  • No default login, nothing shared. Upstream Casdoor 4.x either creates the built-in admin with no password, waiting for the first visitor to set one on a welcome page, or (in demo mode) creates it with the well-known 123. On this image, first boot sets a per-instance administrator password before nginx starts, and proves that 123, an empty password and the welcome page are all refused. The token signing key (cert-built-in, 4096-bit RSA) and the built-in application's client secret are also generated on each instance. The image contains no database and no secret. Casdoor and nginx refuse to start until first boot has created them.
  • Closed by default. Self-signup is off for the built-in application (users of the built-in organisation are global administrators). Casdoor's LDAP server (ports 389/636) and RADIUS server (1812/udp) are switched off, as are the in-app version check and self-upgrade.
  • Fixed host name, real client address. Casdoor is handed the configured name, never the name a client sends. Requests for any other host name are dropped, and the client address Casdoor records is the real TCP peer, never a forwarded header.
  • Locked down serving. TLS 1.2 and 1.3 only. The admin endpoints that fetch from the internet (/api/scan, /api/get-upgrade-info) refuse cross-site requests. Casdoor, nginx and the first-boot service are blocked from the instance metadata service, so a webhook or provider URL pointed at 169.254.169.254 gets nothing. The AMI requires IMDSv2.
  • A current Go build of the official release. See the build section below.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

The image contains Casdoor 4.24.0, nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent. Casdoor is at /opt/casdoor/casdoor, a SecureAnyCloud rebuild of the upstream v4.24.0 tag. The web console web/build and conf/app.conf come from the official v4.24.0 release, byte for byte.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route that you use for management).
  • A security group that allows TCP 22 (SSH) from your own networks only. It must also allow TCP 443 (the console and the sign-in endpoints) and TCP 80 (redirects to HTTPS) from the networks your users and applications reach Casdoor from. Do not open 8000, because Casdoor listens on the loopback address only.
  • For production, a DNS name you control (for example auth.example.com). Your applications store Casdoor's issuer URL, so it should not change. The EC2 public name changes when the instance is stopped and started without an Elastic IP. WebAuthn passkeys also need a real DNS name.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Casdoor 4.24.0 on Ubuntu 24.04 as the software version and pick your Region, then choose Launch through EC2. Choose m5.large, your key pair, and a security group as described above.

To launch with the AWS CLI instead, replace the values in angle brackets:

aws ec2 run-instances --image-id <ami-id-from-the-listing> --instance-type m5.large \
  --key-name <your-key-pair> --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=casdoor}]'

First boot takes a few seconds after the instance is running.

2

Connect to your instance

For Ubuntu 24.04, the SSH user is ubuntu.

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

systemctl is-active casdoor-netconf casdoor-firstboot casdoor nginx
active active active active

casdoor-netconf runs on every boot and sets the name that the instance is served as. casdoor-firstboot ran once and created the database.

Only SSH, HTTP and HTTPS are public. Casdoor listens on the loopback address, and nothing listens on the LDAP ports:

sudo ss -tln | grep -E ':(22|80|443|8000|389|636) '
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* LISTEN 0 511 0.0.0.0:80 0.0.0.0:* LISTEN 0 4096 127.0.0.1:8000 0.0.0.0:* LISTEN 0 511 0.0.0.0:443 0.0.0.0:* LISTEN 0 4096 [::]:22 [::]:* LISTEN 0 511 [::]:80 [::]:* LISTEN 0 511 [::]:443 [::]:*

The RADIUS server is off too, with no listener on 1812/udp:

sudo ss -uln | grep -c ':1812 ' || true
0

The OIDC discovery document answers through nginx. The served name is in /run/sac-casdoor/instance.env. Until you set your own name (Step 10), it is the instance's EC2 public DNS name:

. /run/sac-casdoor/instance.env; curl -s --cacert /etc/nginx/tls/casdoor.crt --resolve "$CASDOOR_HOST:443:127.0.0.1" "https://$CASDOOR_HOST/.well-known/openid-configuration" | python3 -c 'import json,sys; d=json.load(sys.stdin); print(d["issuer"]); print(d["authorization_endpoint"]); print(d["token_endpoint"]); print(d["jwks_uri"]); print(d["end_session_endpoint"])'
https://ec2-3-86-188-214.compute-1.amazonaws.com https://ec2-3-86-188-214.compute-1.amazonaws.com/login/oauth/authorize https://ec2-3-86-188-214.compute-1.amazonaws.com/api/login/oauth/access_token https://ec2-3-86-188-214.compute-1.amazonaws.com/.well-known/jwks https://ec2-3-86-188-214.compute-1.amazonaws.com/api/logout
4

Get your password and the certificate fingerprint

sudo cat /root/casdoor-credentials.txt

The file (root only, mode 0600) gives the console address and the administrator user admin of the organisation built-in. It also gives that user's per-instance password (shown here as <redacted>) and the SHA-256 fingerprints of this instance's TLS certificate and token signing certificate:

# Casdoor 4.24.0 - generated on first boot by casdoor-firstboot.service. Unique to this instance. Store it safely. casdoor.url=https://ec2-3-86-188-214.compute-1.amazonaws.com/ casdoor.admin_user=admin casdoor.organization=built-in casdoor.admin_password=<redacted> casdoor.tls_sha256_fingerprint=58:41:E1:8B:7A:81:54:89:85:76:74:6E:40:5E:E3:CC:4F:DA:AF:76:92:FB:A1:A7:87:AA:45:60:43:D4:94:42 casdoor.jwt_cert_sha256_fingerprint=8E:33:0C:1A:2C:56:F0:6F:81:2E:9C:B6:00:61:F3:9B:9C:94:0D:A0:5F:59:51:7A:70:00:94:86:9A:92:86:5E

Every instance gets its own password, TLS certificate and token signing key. Casdoor stores the password as a bcrypt hash.

5

Sign in to the console

Browse to casdoor.url from Step 4. The certificate is self-signed and unique to your instance, so your browser warns about it. Open the certificate details in the browser, check that its SHA-256 fingerprint matches casdoor.tls_sha256_fingerprint, and only then continue.

Do not add the certificate to your operating system's trust store. For production, replace it with a certificate from a certificate authority (Step 10). By design, requests that use any other name for the instance are dropped. An example is a bare IP address that is not the instance's own.

The sign-in page has no sign-up link, because self-signup is off.

Sign in as admin with the password from Step 4. The console opens on the dashboard.

If you want a password of your own, change the administrator password from the account menu (top right, My Account). The "Forgot password?" link needs an e-mail or SMS provider, which is not configured.

6

Check the sign-in from the command line

The image includes a check that runs the whole flow through nginx on the instance. It confirms that the discovery document names this instance as the issuer, the upstream default admin/123 is refused, the administrator signs in, the session opens the account, and an anonymous account request is refused. The password reaches the tools in a root-only temporary file, never on a command line:

sudo /usr/local/sbin/casdoor-selftest
CASDOOR_SELFTEST_OK issuer=https://ec2-3-86-188-214.compute-1.amazonaws.com default-123=refused admin=ok session=ok anonymous=refused
7

Organisations, users and applications

A new instance holds the built-in organisation, its administrator and the built-in application (app-built-in, the console itself). Everyone in the built-in organisation is a global administrator of the whole Casdoor instance, so do not add ordinary users there. The Users page shows the one account.

To set up your own users:

  • User Management > Organizations > Add: create an organisation for them. Choose the password policy and the password hashing type there.
  • Identity > Applications > Add: create an application in that organisation for each website or service that signs in through Casdoor. On its Basic tab, set the Redirect URLs that your application uses. Casdoor generates its Client ID and Client secret. Turn on Enable signup on the Authentication tab only if you want people to register themselves for that application.
  • User Management > Users > Add: create users in the new organisation, or let them sign up through an application that allows it.

On the built-in application, Enable signup is off, as first boot left it.

The token signing certificate cert-built-in was generated on this instance's first boot (Identity > Certs). Applications verify Casdoor's tokens with its public key from the JWKS endpoint:

. /run/sac-casdoor/instance.env; curl -s --cacert /etc/nginx/tls/casdoor.crt --resolve "$CASDOOR_HOST:443:127.0.0.1" "https://$CASDOOR_HOST/.well-known/jwks" | python3 -c 'import json,sys; k=json.load(sys.stdin)["keys"]; print(len(k), k[0]["kty"], k[0]["alg"], k[0]["kid"])'
1 RSA RS256 cert-built-in
8

Connect an application

For an OpenID Connect or OAuth 2.0 client, give it:

  • Issuer / discovery URL: https://<your Casdoor name>/.well-known/openid-configuration (Step 3)
  • Client ID and Client secret: from the application you created in Step 7
  • Redirect URI: one of the Redirect URLs you set on that application

Most libraries configure everything else from the discovery document. For SAML, the application's SAML tab gives the IdP metadata URL. For CAS, see the Casdoor documentation.

Until you use a CA-issued certificate (Step 10), your application has to trust this instance's self-signed certificate for its back-channel calls (token and userinfo endpoints). Pin the certificate file /etc/nginx/tls/casdoor.crt in that application only, not in an operating system trust store.

9

Check the security posture

These commands show the build, and that root owns the binary and its directory. Nothing running as the casdoor user can replace the binary, and Casdoor's own self-upgrade fails closed:

head -6 /opt/casdoor/BUILDINFO; sha256sum /opt/casdoor/casdoor; stat -c '%U:%G %a %n' /opt/casdoor /opt/casdoor/casdoor
# Casdoor 4.24.0 - a sac rebuild of the pinned upstream tag v4.24.0 (commit 07e91923a4177a3e4a810f9c75323de38660fd70)
# source https://github.com/casdoor/casdoor/archive/refs/tags/v4.24.0.tar.gz (sha256 deefb00be8696537edc2e97020e68a73edfeb091765f012da4fdae36a3439ce7), toolchain https://go.dev/dl/go1.27.3.linux-amd64.tar.gz (sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40)
# minor module bumps (no source change): golang.org/x/crypto@v0.58.0 golang.org/x/net@v0.62.0 github.com/corazawaf/coraza/v3@v3.8.1 github.com/redis/go-redis/v9@v9.23.0 go.opentelemetry.io/otel/sdk@v1.47.0 github.com/aws/aws-sdk-go-v2/service/s3@v1.114.6 filippo.io/edwards25519@v1.2.0 github.com/slack-go/slack@v0.32.0 github.com/Azure/go-ntlmssp@v0.1.1
# go build -trimpath -buildvcs=false -ldflags '-s -w -buildid= -X github.com/casdoor/casdoor/util.Version=v4.24.0 -X github.com/casdoor/casdoor/util.CommitId=07e91923a4177a3e4a810f9c75323de38660fd70' (go version -m omits -ldflags under -trimpath)
# binary sha256 e55a46c722561807e7759d81beaf2bccc3b92795775ccf2484d68ebf1bc1199a (reproducible; equals the out-of-band builds) /opt/casdoor/casdoor: go1.27.3 e55a46c722561807e7759d81beaf2bccc3b92795775ccf2484d68ebf1bc1199a /opt/casdoor/casdoor root:root 755 /opt/casdoor root:root 755 /opt/casdoor/casdoor

Check the listener, origin, LDAP, RADIUS, demo-mode, upgrade and proxy settings. The first-boot administrator password is no longer in the file, so the count is 0:

sudo grep -E '^(httpaddr|httpport|origin|ldapServerPort|ldapsServerPort|radiusServerPort|isDemoMode|disableVersionCheck|disableUpgrade|trustedProxies) ' /opt/casdoor/conf/app.conf;
sudo grep -c '^initAdminPassword' /opt/casdoor/conf/app.conf || true
httpport = 8000 origin = https://ec2-3-86-188-214.compute-1.amazonaws.com isDemoMode = false ldapServerPort = 0 ldapsServerPort = 0 radiusServerPort = off httpaddr = 127.0.0.1 disableVersionCheck = true disableUpgrade = true trustedProxies = 127.0.0.1/32 0

The RADIUS server is switched off by its setting. Casdoor logs this once each time it starts:

sudo journalctl -u casdoor --no-pager -o cat --grep 'StartRadiusServer\(\) failed' | tail -1
2026/10/29 00:00:47 StartRadiusServer() failed, err = listen udp: lookup udp/off: unknown port

Casdoor cannot reach the instance metadata service. The same check from your own shell answers 200:

sudo bash -c 'cg=$(systemctl show -p ControlGroup --value casdoor.service); echo $$ > /sys/fs/cgroup$cg/cgroup.procs; setpriv --reuid=casdoor --regid=casdoor --init-groups curl -s -m 5 -o /dev/null -w "imds from casdoor: %{http_code}\n" -X PUT -H "X-aws-ec2-metadata-token-ttl-seconds: 60" http://169.254.169.254/latest/api/token; true' | grep -x 'imds from casdoor: 000'
imds from casdoor: 000

Port 80 redirects to HTTPS on the configured name, and a request for another host name is dropped. 000 means nginx closed the connection:

. /run/sac-casdoor/instance.env; curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' --resolve "$CASDOOR_HOST:80:127.0.0.1" "http://$CASDOOR_HOST/login"; curl -sk -o /dev/null -w '%{http_code}\n' -H 'Host: evil.example' https://127.0.0.1/ || true
301 https://ec2-3-86-188-214.compute-1.amazonaws.com/login 000

The certificate is an end-entity certificate for this instance's names:

openssl x509 -in /etc/nginx/tls/casdoor.crt -noout -subject -ext basicConstraints,subjectAltName
subject=CN = ec2-3-86-188-214.compute-1.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-3-86-188-214.compute-1.amazonaws.com, IP Address:3.86.188.214, IP Address:172.31.84.161, IP Address:127.0.0.1, DNS:localhost X509v3 Basic Constraints: critical CA:FALSE
10

Use your own DNS name and certificate

Point a DNS record (for example auth.example.com) at the instance, then set the name. An Elastic IP keeps the address across stop and start. The name becomes the console address and the OIDC issuer, the certificate is re-issued for it, and nginx accepts only that name and the instance's own addresses:

sudo casdoor-set-hostname auth.example.com

The name is kept in /etc/sac/casdoor-hostname across reboots. Afterwards, update the issuer and redirect settings in your applications.

To use a certificate from a certificate authority, replace /etc/nginx/tls/casdoor.crt and /etc/nginx/tls/casdoor.key (the key root only, mode 0600) with yours for the same name, then run sudo systemctl reload nginx. Run casdoor-set-hostname again only if you change the name, because it issues a new self-signed certificate.

11

Your data and backups

Everything Casdoor stores (organisations, users and their password hashes, applications and client secrets, certificates and keys, tokens, sessions, audit records) is in /var/lib/casdoor (owned by the casdoor user, mode 0700). The settings are in /opt/casdoor/conf/app.conf.

To take a consistent copy of the database while Casdoor runs, use SQLite's online backup through Python's standard library:

sudo bash -c 'umask 077; python3 -c "import sqlite3; s = sqlite3.connect(\"file:/var/lib/casdoor/casdoor.db?mode=ro\", uri=True); d = sqlite3.connect(\"/root/casdoor-backup.db\"); s.backup(d); d.close()"';
sudo stat -c '%U %a %n' /root/casdoor-backup.db
root 600 /root/casdoor-backup.db

Copy the backup off the instance and keep it as safe as the credentials, because it holds the token signing key and the client secrets. To restore, stop Casdoor, put the file back as /var/lib/casdoor/casdoor.db owned by casdoor, and start Casdoor. EBS snapshots of the instance work too.

The Casdoor build on this image

Upstream's official v4.24.0 binary is built with Go 1.25.8, a Go release line that no longer receives security fixes. In that build, govulncheck finds 52 Go standard-library and library advisories reachable from Casdoor's code. Several are on unauthenticated request paths such as the reverse proxy, the static file filter, e-mail validation and SAML parsing.

This image therefore ships a SecureAnyCloud rebuild of the pinned upstream tag:

  • The source of the v4.24.0 tag (commit 07e91923a417), unchanged, with no source patches.
  • It is compiled with the official Go 1.27.3 toolchain and CGO_ENABLED=0, reproducibly. Three independent builds gave the same bytes, and the sha256 is in /opt/casdoor/BUILDINFO.
  • It has minor updates of these libraries only, inside their current major versions: golang.org/x/crypto 0.58.0, golang.org/x/net 0.62.0, github.com/corazawaf/coraza/v3 3.8.1, github.com/redis/go-redis/v9 9.23.0, go.opentelemetry.io/otel/sdk 1.47.0, github.com/aws/aws-sdk-go-v2/service/s3 1.114.6, filippo.io/edwards25519 1.2.0, github.com/slack-go/slack 0.32.0 and github.com/Azure/go-ntlmssp 0.1.1. The exact go.mod difference is /opt/casdoor/go.mod.sac.diff.
  • govulncheck on the rebuild finds no advisory reachable from Casdoor's code. Casdoor's own unit tests give the same results as with the release toolchain.

The web console (/opt/casdoor/web/build) and the default conf/app.conf are the files of the official v4.24.0 release, byte for byte. The Casdoor licence (Apache-2.0) is in /opt/casdoor/LICENSE.

The build is not an upstream release binary, so Casdoor's in-app upgrade is refused and switched off. New Casdoor versions arrive as new versions of this listing.

Known residual advisories. None of them is reachable by an unauthenticated visitor:

  • SheetJS xlsx 0.18.5 (in the web console): CVE-2023-30533 (prototype pollution) and CVE-2024-22363 (ReDoS), both HIGH. There is no fixed version on npm. Only an administrator who imports a crafted .xlsx file (Users > Upload (.xlsx)) can reach it, inside that administrator's own browser. Import only files you trust.
  • dompurify 3.4.16 (web console): GHSA-cg2g-xh64-v4q7 and GHSA-mrv2-3pqr-j5jr. Fixed upstream in 3.4.17, but the fix needs a console rebuild. Not reachable, because Casdoor does not use the affected options.
  • react-router 6.30.6 and cookie 0.5.0 (web console): CVE-2026-53666 and CVE-2026-53669 (medium), and CVE-2024-47764 (low). Fixed upstream, but the fix needs a console rebuild. Client-side only.
  • aws-sdk-go v1 S3 crypto, go-jose v2 and x/crypto/openpgp (in the server): GO-2022-0635, GO-2022-0646, GO-2024-2631 and GO-2026-5932. There is no fixed version of these deprecated packages. They are linked into the binary but never called by Casdoor (govulncheck call graph).

Two more things to know. First, the sign-in pages load Casdoor's logo and icons from https://cdn.casbin.org (the staticBaseUrl setting) in your users' browsers. Second, the logout endpoints (/api/logout, /api/sso-logout) accept cross-site requests by design, because OpenID Connect RP-initiated logout comes from your applications' sites. They only end the browser's own Casdoor session.

Support

SecureAnyCloud provides support for this image through the AWS Marketplace listing. Casdoor's own documentation is at casdoor.org.

Deploy on AWS

Launch Casdoor 4.24.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS