greenpau / caddy-security

πŸ” Authentication, Authorization, and Accounting (AAA) App and Plugin for Caddy v2. πŸ’Ž Implements Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0 (Github, Google, Facebook, Okta, etc.), SAML Authentication. MFA/2FA with App Authenticators and Yubico. πŸ’Ž Authorization with JWT/PASETO tokens. πŸ”
https://authcrunch.com/
Apache License 2.0
1.49k stars 73 forks source link

reference-config: multi-host domain with LinkedIn and Local authentication #353

Open greenpau opened 4 months ago

greenpau commented 4 months ago

This issue documents reference config for multi-host domain with LinkedIn and Local authentication.

This configuration is related to an application I am building and demonstrates how things work. It has little config gems, e.g. on_demand_tls.

Please do not comment on this issue with comments. Rather, open new issues and reference this one.

Upon log in, a LinkedIn-authenticated user sees the following portal.

image

A locally-authenticated user sees the following portal.

image

{
    http_port 8080
    https_port 8443
    admin off
    # debug

    on_demand_tls {
        ask http://localhost:5555/
    }

    order trace before respond
    order authenticate before respond
    order authorize before basicauth

    security {
        oauth identity provider linkedin {
            realm linkedin
            driver linkedin
            client_id {env.LINKEDIN_APP_CLIENT_ID}
            client_secret {env.LINKEDIN_APP_CLIENT_SECRET}
            icon linkedin priority 100
        }

        local identity store localdb {
            realm local
            path {env.LOCAL_DATA_PATH}userdb/users.json
            user {env.USERS_ADMIN_USERNAME} {
                name "{env.USERS_ADMIN_NAME}"
                email "{env.USERS_ADMIN_EMAIL}"
                password "{env.USERS_ADMIN_SECRET}" overwrite
                roles "authp/admin" "authp/user"
            }
            user {env.USERS_STDUSER_USERNAME} {
                name "{env.USERS_STDUSER_NAME}"
                email "{env.USERS_STDUSER_EMAIL}"
                password "{env.USERS_STDUSER_SECRET}"
                roles "authp/admin" "authp/user" "authp/dbadmin"
            }
        }

        authentication portal myportal {
            crypto default token lifetime 86400
            # crypto default token lifetime 7200
            crypto key sign-verify {env.JWT_SHARED_KEY}
            # cookie domain {env.GLOBAL_APP_DOMAIN}
            # cookie domain {env.LOCAL_APP_NAME}.{env.LOCAL_APP_DOMAIN}
            # cookie domain {env.GLOBAL_APP_NAME}.{env.GLOBAL_APP_DOMAIN}
            cookie domain {env.GLOBAL_APP_DOMAIN}
            cookie lifetime 86400
            ui {
                meta title "BizDevStack Authentication Portal"
                meta author "BizDevStack, Inc."
                meta description "Authentication Portal for Business Development Stack (BDS) Users."
                static_asset "assets/images/bizdevstack_banner.jpg" "images/jpg" ui/banner.jpg
                static_asset "assets/images/bizdevstack_logo.png" "images/png" ui/logo.png
                static_asset "assets/images/favicon.png" "images/png" ui/favicon.png
                template login ui/login.template
                template sandbox ui/sandbox.template
                template portal ui/portal.template
                template whoami ui/whoami.template
                links {
                    "My Apps" https://go.{env.GLOBAL_APP_DOMAIN}/ icon "las la-sitemap"
                    "Copilot" https://go.{env.GLOBAL_APP_DOMAIN}/apps/cpl/ icon "las la-rocket"
                }
            }
            transform user {
                match realm local
                action add role authp/user bdsapps/user bdsapps/cpl/user
                ui link "My Identity" "/auth/whoami" icon "las la-id-badge"
                ui link "My Profile" "/auth/profile/" icon "las la-user"
            }
            transform user {
                match role "authp/dbadmin"
                ui link "Database" https://go.{env.GLOBAL_APP_DOMAIN}/pgadmin4/ icon "las la-database"
            }
            transform user {
                match realm linkedin
                action add role authp/user bdsapps/user bdsapps/cpl/user
            }
            enable identity provider linkedin
            enable identity store localdb
        }

        authorization policy db_mgmt_access {
            set auth url https://auth.myfiosgateway.com/auth/
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                comment allow admins and users
                match role authp/dbadmin
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }

        authorization policy apps_default_access {
            set auth url https://auth.myfiosgateway.com/auth/
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                comment allow admins and users
                match role authp/admin authp/user bdsapps/user
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }

        authorization policy apps_cpl_access {
            set auth url https://auth.myfiosgateway.com/auth/
            set forbidden url /forbidden.html
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                match role bdsapps/cpl/user
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }

        authorization policy api_cpl_access {
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                match role bdsapps/cpl/user
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }

        authorization policy apps_any_access {
            set auth url https://auth.myfiosgateway.com/auth/
            set forbidden url /forbidden.html
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                match role bdsapps/admin
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }

        authorization policy api_any_access {
            crypto key sign-verify {env.JWT_SHARED_KEY}
            acl rule {
                match role bdsapps/admin
                allow stop
            }
            acl rule {
                comment default deny
                match any
                deny stop log warn
            }
        }
    }
}

http://localhost:5555 {
    bind 127.0.0.1
    # trace tag="tls-on-demand"
    @allowed_localhost query domain=localhost
    @allowed_local query domain={env.LOCAL_APP_NAME}.{env.LOCAL_APP_DOMAIN}
    @allowed_global query domain={env.GLOBAL_APP_NAME}.{env.GLOBAL_APP_DOMAIN}
    @allowed_apps query domain=go.{env.GLOBAL_APP_DOMAIN}
    respond @allowed_localhost 200
    respond @allowed_local 200
    respond @allowed_global 200
    respond @allowed_apps 200
    respond 400
}

:8080 {
    redir https://{hostport}{uri} 302
}

{env.GLOBAL_APP_NAME}.{env.GLOBAL_APP_DOMAIN}:8443 {
    tls internal {
        on_demand
    }

    header {
        Access-Control-Allow-Origin https://go.{env.GLOBAL_APP_DOMAIN}
        Access-Control-Allow-Credentials true
        Access-Control-Allow-Methods *
        Access-Control-Allow-Headers *
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
    }

    route /auth* {
        authenticate with myportal
    }

    route /* {
        redir https://{hostport}/auth/ 302
    }
}

go.{env.GLOBAL_APP_DOMAIN}:8443 {
    tls internal {
        on_demand
    }

    # header {
    #   X-Content-Type-Options nosniff
    #   X-Frame-Options DENY
    # }

    handle_errors {
        # Not found
        @status-code-404 `{err.status_code} in [404, 410]`
        handle @status-code-404 {
            rewrite * /notfound.html
            file_server {
                root {env.APP_STATIC_ROOT}
            }
        }

        # Access Forbidden
        @status-code-403 `{err.status_code} in [403]`
        handle @status-code-403 {
            rewrite * /forbidden.html
            file_server {
                root {env.APP_STATIC_ROOT}
            }
        }

        # Bad Gateway
        @status-code-502 `{err.status_code} in [502]`
        handle @status-code-502 {
            rewrite * /bad_gateway.html
            file_server {
                root {env.APP_STATIC_ROOT}
            }
        }

        # All other errors
        handle {
            rewrite * /other.html
            file_server {
                root {env.APP_STATIC_ROOT}
            }
        }
    }

    route /pgadmin4* {
        authorize with db_mgmt_access
        reverse_proxy http://127.0.0.1:8180
    }

    route /api/mst* {
        authorize with api_any_access
        respond "Not Implemented"
    }

    route /apps/mst* {
        authorize with apps_any_access
        respond "Not Implemented"
    }

    route /api/rmg* {
        authorize with api_any_access
        respond "Not Implemented"
    }

    route /apps/rmg* {
        authorize with apps_any_access
        respond "Not Implemented"
    }

    # Flask API Server
    route /api/cpl* {
        authorize with api_cpl_access
        # Start: backend/services/api/run_server.sh
        reverse_proxy http://127.0.0.1:5001
    }

    # Vite Development Server
    route /apps/cpl* {
        authorize with apps_cpl_access
        # Start: frontend/scripts/app_dev.sh cpl
        reverse_proxy http://127.0.0.1:3000
    }

    # Vite Build Files
    # route /apps/cpl* {
    #     authorize with portal_policy
    #     uri strip_prefix /auth/profile
    #     file_server {
    #         root {$HOME}/dev/go/src/github.com/authcrunch/authcrunch-ui/frontend/profile/build/
    #         pass_thru
    #     }
    #     rewrite * /index.html
    #     file_server {
    #         root {$HOME}/dev/go/src/github.com/authcrunch/authcrunch-ui/frontend/profile/build/
    #         pass_thru
    #     }
    # }

    route /* {
        authorize with apps_default_access
        file_server {
            root {env.APP_STATIC_ROOT}
        }
    }
}

The environment variables references in the above config follow. I usually store then in .env file:

#
# Authentication Portal
#

export JWT_SHARED_KEY="11612123-b228-4caa-8fb9-56fb4517777b"
export LOCAL_DATA_PATH=./tmp/
export LOCAL_APP_NAME="auth"
export LOCAL_APP_DOMAIN="myfiosgateway.com"
export GLOBAL_APP_NAME="auth"
export GLOBAL_APP_DOMAIN="myfiosgateway.com"

#
# LinkedIn
#
export LINKEDIN_APP_CLIENT_ID="slk38cms"
export LINKEDIN_APP_CLIENT_SECRET="kljdssssklfjlkjflsdfjsd"

#
# Users
#

export USERS_ADMIN_NAME="Judy Morton"
export USERS_ADMIN_USERNAME="jmorton"
export USERS_ADMIN_EMAIL=jmorton@localhost.localdomain
# echo -n "Auth@c50691b7" | bcrypt-cli -c 10
export USERS_ADMIN_SECRET="bcrypt:10:\$2a\$10\$XJe2Fa5VRs9rHazIF4YXgO4BhvWjCSu4XwBs7/nOtz.GAKAeTYk2G"

export USERS_STDUSER_NAME="Jake Owens"
export USERS_STDUSER_USERNAME="jowens"
export USERS_STDUSER_EMAIL=jowens@localhost.localdomain
# echo -n "Auth@c50691b7" | bcrypt-cli -c 10
export USERS_STDUSER_SECRET="bcrypt:10:\$2a\$10\$XJe2Fa5VRs9rHazIF4YXgO4BhvWjCSu4XwBs7/nOtz.GAKAeTYk2G"