Security AWS

Casdoor 4.24.0 on AWS User Guide

| Product: Casdoor 4.24.0 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Casdoor 4.24.0 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg 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; it manages organisations, users, groups, roles and permissions, supports passwords, one-time codes, WebAuthn passkeys and multi-factor authentication, and can federate sign-in to other identity providers.

The cloudimg image is a working identity server out of the box:

  • 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) with the well-known 123. On this image the 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. The in-app version check and self-upgrade are switched off.
  • 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 below.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Casdoor 4.24.0 (/opt/casdoor/casdoor, a cloudimg rebuild of the upstream v4.24.0 tag; the web console web/build and conf/app.conf from the official v4.24.0 release, byte for byte), nginx, Ubuntu 24.04 LTS, cloud-init and the AWS Systems Manager agent.

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 you use for management).
  • A security group that allows TCP 22 (SSH) from your own networks only, and 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: 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.

Step 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 your Region, then Launch through EC2. Choose m5.large, your key pair, and a security group as above.

With the AWS CLI (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.

Step 2: Connect to your instance

Operating system SSH user
Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 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 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 (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/cloudimg-casdoor/instance.env; until you set your own (Step 10) it is the instance's EC2 public DNS name:

. /run/cloudimg-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

Step 4: Get your password and the certificate fingerprint

sudo cat /root/casdoor-credentials.txt

The file (root only, mode 0600) gives the console address, the administrator user admin of the organisation built-in, its 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.

Step 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). Requests that use any other name for the instance (for example a bare IP address that is not the instance's own) are dropped by design.

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

The Casdoor sign-in page with the Password, Code, WebAuthn and Face ID tabs, the Username and Password fields and the Sign In button, and no sign-up link

Sign in as admin with the password from Step 4:

The Casdoor sign-in page with the username admin and a masked password entered

The console opens on the dashboard:

The Casdoor dashboard showing one user, one organisation, one application and three providers, with the navigation for User Management, Identity, Authorization, Auditing and Admin

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

Step 6: Check the sign-in from the command line

The image includes a check that runs the whole flow through nginx on the instance: 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 is passed to 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

Step 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:

The Casdoor Users page listing a single user, admin, in the built-in organisation and app-built-in application, with Download template, Upload (.xlsx) and Add buttons

For your own users:

  1. User Management > Organizations > Add: create an organisation for them (choose the password policy and the password hashing type there).
  2. 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 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.
  3. 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 the first boot left it:

The Authentication tab of the built-in application app-built-in in the Casdoor console, with the Enable signup switch off

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:

The Casdoor Certs page listing the single certificate cert-built-in (Built-in Cert, JWT scope), created on the instance's first boot

. /run/cloudimg-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

Step 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.

Step 9: Check the security posture

The build, and that the binary and its directory belong to root (so nothing running as the casdoor user can replace it, 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 cloudimg 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

The listener, origin, LDAP, RADIUS, demo-mode, upgrade and proxy settings. The first-boot administrator password is no longer in the file (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 it 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/cloudimg-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 = cloudimg
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

Step 10: Use your own DNS name and certificate

Point a DNS record (for example auth.example.com) at the instance (an Elastic IP keeps the address across stop and start), then set the name. It 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/cloudimg/casdoor-hostname across reboots. Update the issuer and redirect settings in your applications afterwards. 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 and run sudo systemctl reload nginx. Run casdoor-set-hostname again only if you change the name, since it issues a new self-signed certificate.

Step 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 (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 safely as the credentials: it holds the token signing key and the client secrets. Restore by stopping Casdoor, putting the file back as /var/lib/casdoor/casdoor.db owned by casdoor, and starting 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; govulncheck finds 52 Go standard-library and library advisories reachable from Casdoor's code in that build (several on unauthenticated request paths such as the reverse proxy, the static file filter, e-mail validation and SAML parsing). This image therefore ships a cloudimg rebuild of the pinned upstream tag:

  • the source of the v4.24.0 tag (commit 07e91923a417), unchanged: no source patches;
  • compiled with the official Go 1.27.3 toolchain, CGO_ENABLED=0, reproducibly (the same bytes on three independent builds; the sha256 is in /opt/casdoor/BUILDINFO);
  • with 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.cloudimg.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. Because the build is not an upstream release binary, 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 reachable by an unauthenticated visitor:

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

Two more things to know: the sign-in pages load Casdoor's logo and icons from https://cdn.casbin.org (the staticBaseUrl setting) in your users' browsers; and 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

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