David,
Thanks for the fantastic write-up!  I think using CAS only for Guacamole
2FA is probably overkill for most folks, but if you're already using CAS or
want CAS for 2FA for other stuff, and want to integrate Guacamole into it,
this is great.  I'll try to take a look at your write-up in detail and
provide some suggested edits for it.  Don't know exactly where the best
place to post it would be, but I definitely think it should be made
available!  I think it probably should be made available to a wider
audience than just the Guacamole folks, as I think it's probably useful for
folks who want to do 2FA with CAS, in general.

-Nick

On Fri, Sep 29, 2017 at 6:09 PM, David Bonnes <da...@bonnes.name> wrote:

> For humor, I set up a Apereo CAS server as a means to use gauth/TOTP as a
> second-factor for authenticating with guacmole.  It was working 100%, but
> personally, I'll be sticking with DUO for now.  However, I think some
> people would want this feature.
>
> I really think the method I used needs writing up somewhere for the
> benefit of the community (and doubtless for them to improve), but I am not
> the person to do that...
>
> Is someone willing to edit my notes, and post a nice tutorial somewhere?
> For the right person (i.e. some evidence you'll write up a nice how-to), I
> am willing to take some time to explain what worked, what didn't, and why.
>
> If not, and in any case, here is the bulk of my notes/scripts...
>
>
> #!/bin/bash
> ############################################################
> ########################################################################
> #0. Confirm that Guacamole is working with MySQL (have something in the
> profile)
> #1. Test basic config of CAS via CAS - need to set log folder
> #2. Switch to static account (same name as one in 0.) via CAS - consider
> SHA256 encoding
> #3. Test auth through guacuamole - should see profile (will need service
> registry)
> #4. Switch to jdbc auth (QUERY) on CAS - (may need to set permission for
> guac_username) - can test auth-d via cas logon page first
> #5. As above, but with Gauth
> ############################################################
> ########################################################################
>
> ############################################################
> ########################################################################
> ## Install CAS webapp via the overlay method
> # can change <cas.version> in pom.xml for other versions...
>   mkdir /opt; cd /opt
>   git clone -b 5.2 https://github.com/apereo/cas-overlay-template cas
>   cd cas
>   chmod a+x build.sh
>
>
>
>
> ############################################################
> ########################################################################
> # To eliminate: "Non-secure Connection" warning, add secure="true" to 8080
> of /var/lib/tomcat8/conf/server.xml
>
> ### ./etc/cas/config/log4j2.xml: set <Property name="cas.log.dir">/var/log/
> tomcat8</Property>
>   sed -i -e '/"cas.log.dir"/ s:>.*<:>/var/log/tomcat8<:'
> etc/cas/config/log4j2.xml
>   mkdir -p /etc/cas/logs; chmod a+w /etc/cas/logs
>
> ### ./pom.xml - will need this eventually
> #       <Property name="cas.version">5.2.0-RC4-SNAPSHOT</Property>
>
> ## ./etc/cas/config/cas.properties
> ## Enable logging...
>   logging.level.org.apereo: TRACE
>   logging.config: file:/etc/cas/config/log4j2.xml
>
> ## Set CAS server name URL...
>   cas.server.name:   https://vm-builder.home:8443
>   cas.server.prefix: ${cas.server.name}/cas
>
> ## Enable basic admin pages...
>   cas.adminPagesSecurity.ip=172\.27\.0\.99
>   cas.monitor.endpoints.enabled=true
>   cas.monitor.endpoints.sensitive=false
>
>
>
> ############################################################
> ########################################################################
>
>
> service tomcat8 stop
> rm    /var/log/tomcat8/*; rm /etc/cas/logs/*
> rm -r /var/lib/tomcat8/webapps/cas; rm /var/lib/tomcat8/webapps/cas.war
> ./build.sh package
> cp /opt/cas/target/cas.war /var/lib/tomcat8/webapps
> cp -r etc/cas/ /etc
> service tomcat8 restart
> tail -f /var/log/tomcat8/catalina.out
>
>
> ############################################################
> ########################################################################
> # see: https://apereo.github.io/cas/5.1.x/installation/Whitelist-
> Authentication.html
>
>
> ### ./etc/cas/config/cas.properties
> ## A whitelist of users (use SHA-256 password hash)...
> # cas.authn.accept.users=dbonnes::P@ssw0rd
>   cas.authn.accept.users=dbonnes::d61bcb77d84080738bd59999993b18
> 1400992e8c272b372bb4e33522427936
>   cas.authn.accept.passwordEncoder.type=DEFAULT
>   cas.authn.accept.passwordEncoder.characterEncoding=UTF-8
>   cas.authn.accept.passwordEncoder.encodingAlgorithm=SHA-256
>
>
>
> ############################################################
> ########################################################################
> # see: https://groups.google.com/a/apereo.org/forum/#!topic/cas-
> user/jJ8OOyoQoBw
>
> ### ./pom.xml
> <dependency>
>     <groupId>org.apereo.cas</groupId>
>     <artifactId>cas-server-support-json-service-registry</artifactId>
>     <version>${cas.version}</version>
> </dependency>
>
> ### ./etc/cas/config/cas.properties
> ## Register default services, including: ^(https|imaps)://.* [ **NOT**
> http://.* ]
> # cas.serviceRegistry.initFromJson=true
> ## Register specified services (this is fixed on/before
> 5.2.0-RC2-SNAPSHOT, but after 5.1.4)
>   cas.serviceRegistry.json.location=file:/etc/cas/services
>
> ### ./etc/cas/services/http-1001.json
>   mkdir -p etc/cas/services
>
>   cat <<EOF > etc/cas/services/http-1001.json
> {
>   "@class" : "org.apereo.cas.services.RegexRegisteredService",
>   "serviceId" : "^(https|http)://.*",
>   "name" : "HTTP(S), for testbeds only",
>   "id" : 1001,
> }
>
> EOF
>
> ## Check via: cat /var/log/tomcat8/cas.log | grep http
>
>
> ############################################################
> ########################################################################
> ## Guac says: *appends* Salt to password, see:
> https://guacamole.incubator.apache.org/doc/gug/jdbc-auth.html
> # -- Generate salt
> SET @salt = UNHEX(SHA2(UUID(), 256));
>
> # -- Create user and hash password with salt
> INSERT INTO guacamole_user (username, password_salt, password_hash)
>      VALUES ('myuser', @salt, UNHEX(SHA2(CONCAT('mypassword',
> HEX(@salt)), 256)));
>
> ## But, CAS says: *prepends* Salt to password
> # "This password encoding method combines the private Salt and the public
> salt which it prepends to the password before hashing."
>
>
> ### ./pom.xml
> <dependency>
>     <groupId>org.apereo.cas</groupId>
>     <artifactId>cas-server-support-jdbc</artifactId>
>     <version>${cas.version}</version>
> </dependency>
>
> <dependency>
>    <groupId>org.apereo.cas</groupId>
>    <artifactId>cas-server-support-jdbc-drivers</artifactId>
>    <version>${cas.version}</version>
> </dependency>
>
> ### ./etc/cas/config/cas.properties [ using QUERY ]
> ## Disable demo accounts (i.e. casuser::Mellon)
>   cas.authn.accept.users=
>
> # Beware: java.sql.SQLException: Access denied for user 
> 'guac_username'@'172.27.0.999'
> (using password: YES)
> ## Using JDBC query authentication...
>   cas.authn.jdbc.query[0].url=jdbc:mysql://lxc-mysql.home:
> 3306/guacamole_db?useSSL=false
>   cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
> # cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQL5Dialect
>   cas.authn.jdbc.query[0].user=guacamole_user
>   cas.authn.jdbc.query[0].password=guac_Passw0rd
>
>   cas.authn.jdbc.query[0].sql=SELECT * FROM guacamole_user WHERE
> username=?
>   cas.authn.jdbc.query[0].fieldDisabled=disabled
>   cas.authn.jdbc.query[0].fieldExpired=expired
>   cas.authn.jdbc.query[0].fieldPassword=username
>
>   cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
>   cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
>   cas.authn.jdbc.encode[0].passwordEncoder.encodingAlgorithm=SHA-256
>
>
>
> ### ./etc/cas/config/cas.properties [ using ENCODE ]
> ## see: https://apereo.github.io/cas/5.1.x/installation/
> Configuration-Properties.html#encode-database-authentication
> ## Using JDBC encode authentication...
>   cas.authn.jdbc.encode[0].url=jdbc:mysql://lxc-mysql.home:
> 3306/guacamole_db?useSSL=false
>   cas.authn.jdbc.encode[0].driverClass=com.mysql.cj.jdbc.Driver
>   cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.MySQL5Dialect
>   cas.authn.jdbc.encode[0].user=guacamole_user
>   cas.authn.jdbc.encode[0].password=guac_Passw0rd
>
>   cas.authn.jdbc.encode[0].sql=SELECT * FROM guacamole_user WHERE
> username=?
>   cas.authn.jdbc.encode[0].fieldDisabled=disabled
>   cas.authn.jdbc.encode[0].fieldExpired=expired
>
> # cas.authn.jdbc.encode[0].numberOfIterations=1
> # cas.authn.jdbc.encode[0].staticSalt=
>   cas.authn.jdbc.encode[0].saltFieldName=password_salt
>   cas.authn.jdbc.encode[0].passwordFieldName=password_hash
>   cas.authn.jdbc.encode[0].algorithmName=SHA-256
>
>
> ## Check via: mysql -u guacamole_user --password=guac_Passw0rd -h
> lxc-mysql.home (from (say) lxc-tomcat: apt install -y mysql-client)
> #  USE guacamole_db;
> #  SELECT username, disabled, expired FROM guacamole_user;
> #  SELECT username, LOWER(hex(password_hash)), hex(password_salt) FROM
> guacamole_user;
>
> echo -n 'P@ssw0rd' | sha256sum
>
> USE guacamole_db;
> SET @username = "myuser2";
> SET @password = "P@ssw0rd";
> INSERT INTO guacamole_user (username, password_salt, password_hash,
> password_date)
>   VALUES (@username, NULL, LOWER(UNHEX(SHA2(@password, 256))), NOW());
>
>
> ############################################################
> ########################################################################
> ############################################################
> ########################################################################
>
> ### ./pom.xml
> <dependency>
>      <groupId>org.apereo.cas</groupId>
>      <artifactId>cas-server-support-gauth</artifactId>
>      <version>${cas.version}</version>
> </dependency>
>
>
> ### ./etc/cas/services/
> # default: failureMode=NOT_SET,bypassEnabled=false
>   cat <<EOF > etc/cas/services/http-1001.json
> {
>
>   "@class" : "org.apereo.cas.services.RegexRegisteredService",
>   "serviceId" : "^(https|http)://.*",
>   "name" : "HTTP(S)/MFA, for testbeds only",
>   "id" : 1001,
>   "multifactorPolicy" : {
>     "@class" : "org.apereo.cas.services.DefaultRegisteredServiceMultif
> actorPolicy",
>     "multifactorAuthenticationProviders" : [ "java.util.LinkedHashSet", [
> "mfa-gauth" ] ],
>     "bypassEnabled" : false,
>     "failureMode" : "CLOSED"
>   }
> }
>
> EOF
>
>
> ### ./etc/cas/config/cas.properties
>
> ## Multifactor Authentication, see: https://apereo.github.io/cas/
> 5.1.x/installation/Configuring-Multifactor-Authentication.html
> ## Activate MFA globally for all, regardless of other (e.g.
> service-specific) settings
>   cas.authn.mfa.globalProviderId=mfa-gauth
> # Describe the global failure mode in case provider cannot be reached
> (CLOSED means Auth fails)
>   cas.authn.mfa.globalFailureMode=CLOSED
>
> ## Configure google authenticator
>   cas.authn.mfa.gauth.windowSize=3
>   cas.authn.mfa.gauth.codeDigits=6
>   cas.authn.mfa.gauth.timeStepSize=30
>   cas.authn.mfa.gauth.trustedDeviceEnabled=true
>   cas.authn.mfa.gauth.issuer=Guacamole
>   cas.authn.mfa.gauth.label=ZXlabelZX
>   cas.authn.mfa.gauth.name=ZXnameZX
>
>
> ## Check via: cat /var/log/tomcat8/catalina.out | grep gauth
>
>
>
> ############################################################
> ########################################################################
> ############################################################
> ########################################################################
>
> ### Create the Guacamole DB/user ('%' instead of 'localhost' for remote
> access) for user accounts/profiles:
>   mysql -u root --password=${sql_root_passwd} <<EOF
> # MySQL script to create JPA DB & user
>   CREATE DATABASE gauth_db;
>   CREATE USER 'gauth_user'@'%' IDENTIFIED BY 'gauth_Passw0rd';
>   GRANT ALL PRIVILEGES ON gauth_db.* TO 'gauth_user'@'%';
>   FLUSH PRIVILEGES;
>   quit
> EOF
>
> ## Check via:  (from (say) lxc-tomcat: apt install -y mysql-client)
> # mysql -u gauth_user --password=gauth_Passw0rd -h lxc-mysql.home
> # USE gauth_db;
> # SHOW tables;
>
>
> ### ./pom.xml (this needs jdbc DB)
> <dependency>
>      <groupId>org.apereo.cas</groupId>
>      <artifactId>cas-server-support-gauth-jpa</artifactId>
>      <version>${cas.version}</version>
> </dependency>
>
>
> ### ./etc/cas/config/cas.properties
> ## jpa
>   cas.authn.mfa.gauth.jpa.database.url=jdbc:mysql://lxc-
> mysql.home:3306/gauth_db?useSSL=false
>   cas.authn.mfa.gauth.jpa.database.driverClass=com.mysql.cj.jdbc.Driver
>   cas.authn.mfa.gauth.jpa.database.dialect=org.hibernate.dialect.
> MySQL5Dialect
>   cas.authn.mfa.gauth.jpa.database.user=gauth_user
>   cas.authn.mfa.gauth.jpa.database.password=gauth_Passw0rd
>   cas.authn.mfa.gauth.jpa.database.healthQuery=SELECT version()
>
> # see: https://stackoverflow.com/questions/42135114/how-does-
> exactly-spring-jpa-hibernate-ddl-auto-property-works-in-spring
> # see: https://github.com/apereo/cas/blob/1e2497e1836e76e42698f050e7e2fc
> d53348af2b/docs/cas-server-documentation/installation/
> Configuration-Properties-Common.md
> # cas.authn.mfa.gauth.jpa.database.ddlAuto=validate | update | create |
> create-drop -- create, then (stop tomcat, then) update?
>   cas.authn.mfa.gauth.jpa.database.ddlAuto=update
>
> # cas.authn.mfa.gauth.jpa.database.ddlAuto=none
> # cas.authn.mfa.gauth.jpa.database.dataSourceName=???
>
>
>
>
> ############################################################
> ########################################################################
> ############################################################
> ########################################################################
>
> #ENDS
>
> ############################################################
> ########################################################################
> ############################################################
> ########################################################################
> ## PreReq: wget -qO- https://api.duosecurity.com/auth/v2/ping | grep OK
> ##         wget -qO- https://api-2bf290a0.duosecurity.com/rest/v1/ping |
> grep OK
>
>
> ### ./pom.xml
> <dependency>
>      <groupId>org.apereo.cas</groupId>
>      <artifactId>cas-server-support-duo</artifactId>
>      <version>${cas.version}</version>
> </dependency>
>
> ## error: NoClassDefFoundError: com/duosecurity/client/Http
> ## see: https://apereo.github.io/cas/5.1.x/installation/
> DuoSecurity-Authentication.html
> ## You may need to add the following repositories to the WAR overlay...
> <repository>
>     <id>duo</id>
>     <url>https://dl.bintray.com/uniconiam/maven</url>
> </repository>
> <repository>
>     <id>duoclient</id>
>     <url>https://jitpack.io</url>
> </repository>
>
>
> ### ./etc/cas/config/cas.properties
>
> ## Activate MFA globally for all, regardless of other settings
>   cas.authn.mfa.globalProviderId=mfa-duo
>
> ## Describe the global failure mode in case provider cannot be reached
> # cas.authn.mfa.globalFailureMode=PHANTOM
>
> ## Settings for duo
>   cas.authn.mfa.duo[0].id=mfa-duo
> # cas.authn.mfa.duo[0].rank=0
>   cas.authn.mfa.duo[0].duoApiHost=api-2bf29099.duosecurity.com
>   cas.authn.mfa.duo[0].duoIntegrationKey=DIN8CV999RGLL77VZ
>   cas.authn.mfa.duo[0].duoSecretKey=23qm5rpZd2hAgRcR1td8fVJi9ww638p099VPBZ
>   cas.authn.mfa.duo[0].duoApplicationKey=fb1c21e199999935b2ab3f999016786
> # cas.authn.mfa.duo[0].trustedDeviceEnabled=true
> # cas.authn.mfa.duo[0].name=
>
> # cas.authn.mfa.duo[0].bypass.principalAttributeName=bypass|skip
> # cas.authn.mfa.duo[0].bypass.principalAttributeValue=true|enabled.+
> # cas.authn.mfa.duo[0].bypass.authenticationAttributeName=bypass|skip
> # cas.authn.mfa.duo[0].bypass.authenticationAttributeValue=
> allowed.+|enabled.+
> # cas.authn.mfa.duo[0].bypass.authenticationHandlerName=AcceptUsers.+
> # cas.authn.mfa.duo[0].bypass.authenticationMethodName=
> LdapAuthentication.+
> # cas.authn.mfa.duo[0].bypass.credentialClassType=UsernamePassword.+
>
>
> # cas.authn.mfa.gauth.json.config.location=file:/somewhere.json
>
>   cat <<EOF > etc/cas/services/http-1001.json
> {
>   "@class" : "org.apereo.cas.services.RegexRegisteredService",
>   "serviceId" : "^(https|http)://.*",
>   "name" : "App Secured by CAS and Duo",
>   "id" : 1001,
>   "description" : "HTTP secured with username/password and Duo MFA
> protection",
>   "attributeReleasePolicy" : {
>     "@class" : "org.apereo.cas.services.ReturnAllAttributeReleasePolicy"
>   },
>
>   "multifactorPolicy" : {
>     "@class" : "org.apereo.cas.services.DefaultRegisteredServiceMultif
> actorPolicy",
>     "multifactorAuthenticationProviders" : [ "java.util.LinkedHashSet", [
> "mfa-duo" ] ],
>     "bypassEnabled" : false,
>     "failureMode" : "CLOSED"
>   },
>
>   "evaluationOrder" : 100
> }
>
> EOF
>
>
>
>
>

Reply via email to