Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 8A731200D21 for ; Sun, 1 Oct 2017 10:49:56 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 88EB01609D9; Sun, 1 Oct 2017 08:49:56 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 912D91609C0 for ; Sun, 1 Oct 2017 10:49:54 +0200 (CEST) Received: (qmail 11270 invoked by uid 500); 1 Oct 2017 08:49:53 -0000 Mailing-List: contact user-help@guacamole.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: user@guacamole.incubator.apache.org Delivered-To: mailing list user@guacamole.incubator.apache.org Received: (qmail 11260 invoked by uid 99); 1 Oct 2017 08:49:53 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 01 Oct 2017 08:49:53 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id A4152D5EDF for ; Sun, 1 Oct 2017 08:49:52 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.264 X-Spam-Level: * X-Spam-Status: No, score=1.264 tagged_above=-999 required=6.31 tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, HTML_MESSAGE=2, KAM_LOTSOFHASH=0.25, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-2.8, RCVD_IN_SORBS_SPAM=0.5, URI_HEX=1.313, WEIRD_PORT=0.001] autolearn=disabled Authentication-Results: spamd1-us-west.apache.org (amavisd-new); dkim=pass (2048-bit key) header.d=bonnes-name.20150623.gappssmtp.com Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id DM8535S2rHMD for ; Sun, 1 Oct 2017 08:49:48 +0000 (UTC) Received: from mail-vk0-f43.google.com (mail-vk0-f43.google.com [209.85.213.43]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTPS id E7B2B5F3FF for ; Sun, 1 Oct 2017 08:49:47 +0000 (UTC) Received: by mail-vk0-f43.google.com with SMTP id 126so1624970vkj.9 for ; Sun, 01 Oct 2017 01:49:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bonnes-name.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to; bh=IWOkbo5zX2Tb48RJSztVolWW46SXZ0DDeqkyP9SV2wo=; b=kdlc9fL1AqHC6YNXL3+ouwMG1xMMMBvodiQztUPnj+WgA3MZiYfKo7ytvOKBSMnqfR etVcu0HFhPt6EdCt5AuO+wS8yEEtD1xULpaFMBDHC620teHOkFpkJd9Wn3ULqs2azaF7 Km0jW/kMvuc+ZiQMayDOJQp2M41MgQXZSbYJqQLCH+8LzGl9QRxyWw8wmMb47rJXfXLg 01jEm98do4AbrdY34nebYdzEetXstzVqGDlXwsRSb2ICXONLbaWXtC7SxACRbbgkXhLh lgmeH5oss4tzDSw2HqWXHEar22TM80LpBYwHYchAaSN+yuSnmyX4613RV3jF5oNYkwVT Fjxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to; bh=IWOkbo5zX2Tb48RJSztVolWW46SXZ0DDeqkyP9SV2wo=; b=Or4xvJEP1pS+nC7P9EgyTh5nafeAIjBtD7XUw7e5fE3BycwyTSG5+at9hqWmA/yIU3 VM3v3ZK3V8UL++NuYfdJ27D099c2gsIflhjkBbQRtxFUzruflAdwGwkLSILlaH/zEWxG oAVpuiFuN/BGYo86+mDEyJagymrxLCTSZgDtssRA0Umj6shaz0gVE5blpdKjhJhOKYJ1 LgIhOWQOIWbwXeMSgOG5VpeBNbms0lZ7wGlmsY9VaM4UlaHZUy75gJYBRzH2oR5Kq0es V8Mlw5MqBR6d1HJpESKvG4ZYR+xmN8S3uWgLk6GwjWYSMBeP7d+lB8wzeOr2Ia2+QbEL SXPw== X-Gm-Message-State: AHPjjUiOfXhyTdC9GrSKcYsIPauMCKCJn/4zAc0ogleaWMfCNB+pFRU8 w6Ep/Y/HBhNH+8FjiXASCqlGjyjgFO8XY2X2xemBHhv5 X-Google-Smtp-Source: AOwi7QA4fHZNw4gxUzqDXq8Ia6wtI/yNw+32AsXp80QqGxeAMfxrkBjIB8KgCkBkEw0br61f2CYyN6cCor3QK84BH/o= X-Received: by 10.31.216.130 with SMTP id p124mr6751603vkg.89.1506847780826; Sun, 01 Oct 2017 01:49:40 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: From: David Bonnes Date: Sun, 01 Oct 2017 08:49:30 +0000 Message-ID: Subject: Re: How-to - Guacamole with Google Authenticator for 2FA To: user@guacamole.incubator.apache.org Content-Type: multipart/alternative; boundary="001a114ec758b5070b055a7856ac" archived-at: Sun, 01 Oct 2017 08:49:56 -0000 --001a114ec758b5070b055a7856ac Content-Type: text/plain; charset="UTF-8" Thanks, For anyone trying, I should warn you that *cas.authn.jdbc.encode* doesn't look compatible with the salted SHA-256 passwords as stored in Guacamole's guacamole_db (which is why I used *cas.authn.jdbc.query[0].fieldPassword=username* (i.e. username rather than password) for my test-bed; in theory, *cas.authn.jdbc.encode* should works with a NULL salt, but I couldn't get that working either (I thought maybe due to upper/lowercase mismatch of hex strings?). My impression of CAS is that it is notoriously difficult to get working due to it's relative paucity of documentation / sample configurations, and its development / release ethos. On that basis, any write-up would benefit CAS even more than Guacamole! The good news regarding TOTP 2FA is that if you look at GUACAMOLE-96 , there may be some good news soon enough. On Sun, 1 Oct 2017 at 02:21 Nick Couchman wrote: > 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 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 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 > name="cas.log.dir">/var/log/tomcat8 >> 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 >> # 5.2.0-RC4-SNAPSHOT >> >> ## ./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::d61bcb77d84080738bd59999993b181400992e8c272b372bb4e33522427936 >> 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 >> >> org.apereo.cas >> cas-server-support-json-service-registry >> ${cas.version} >> >> >> ### ./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 < 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 >> >> org.apereo.cas >> cas-server-support-jdbc >> ${cas.version} >> >> >> >> org.apereo.cas >> cas-server-support-jdbc-drivers >> ${cas.version} >> >> >> ### ./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 >> >> org.apereo.cas >> cas-server-support-gauth >> ${cas.version} >> >> >> >> ### ./etc/cas/services/ >> # default: failureMode=NOT_SET,bypassEnabled=false >> cat < 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.DefaultRegisteredServiceMultifactorPolicy", >> "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} <> # 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) >> >> org.apereo.cas >> cas-server-support-gauth-jpa >> ${cas.version} >> >> >> >> ### ./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/1e2497e1836e76e42698f050e7e2fcd53348af2b/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 >> >> org.apereo.cas >> cas-server-support-duo >> ${cas.version} >> >> >> ## 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... >> >> duo >> https://dl.bintray.com/uniconiam/maven >> >> >> duoclient >> https://jitpack.io >> >> >> >> ### ./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 < 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.DefaultRegisteredServiceMultifactorPolicy", >> "multifactorAuthenticationProviders" : [ "java.util.LinkedHashSet", [ >> "mfa-duo" ] ], >> "bypassEnabled" : false, >> "failureMode" : "CLOSED" >> }, >> >> "evaluationOrder" : 100 >> } >> >> EOF >> >> >> >> >> > --001a114ec758b5070b055a7856ac Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Thanks,

For anyone trying, I should war= n you that cas.authn.jdbc.encode doesn't look compatible with th= e salted SHA-256 passwords as stored in Guacamole's guacamole_db (which= is why I used=C2=A0cas.authn.jdbc.query[0].fieldPassword=3Dusername<= /i>=C2=A0(i.e. username rather than password) for my test-bed; in theor= y,=C2=A0cas.authn.jdbc.encode=C2=A0should works with a NULL salt, bu= t I couldn't get that working either (I thought maybe due to upper/lowe= rcase mismatch of hex strings?).

My impression of = CAS is that it is notoriously difficult to get working due to it's rela= tive paucity of documentation / sample configurations, and its development = / release ethos.=C2=A0 On that basis, any write-up would benefit CAS even m= ore than Guacamole!

The good news regarding TOTP 2= FA is that if you look at=C2=A0GUACAMOLE-96, there may be some good news soon enoug= h.


On Sun, 1 Oct 2017 at 02:21 Nick Couchman <vnick@apache.org> wrote:
David,
Thanks for the fantastic write-up!= =C2=A0 I think using CAS only for Guacamole 2FA is probably overkill for mo= st 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.=C2=A0 I'= ;ll try to take a look at your write-up in detail and provide some suggeste= d edits for it.=C2=A0 Don't know exactly where the best place to post i= t would be, but I definitely think it should be made available!=C2=A0 I thi= nk it probably should be made available to a wider audience than just the G= uacamole folks, as I think it's probably useful for folks who want to d= o 2FA with CAS, in general.

-Nick

On Fri, Sep 29, = 2017 at 6:09 PM, David Bonnes <david@bonnes.name> wrote:
=
For humor, I set up a Apere= o CAS server as a means to use gauth/TOTP as a second-factor for authentica= ting with guacmole.=C2=A0 It was working 100%, but personally, I'll be = sticking with DUO for now.=C2=A0 However, I think some people would want th= is feature.

I really think the method I used needs writi= ng up somewhere for the benefit of the community (and doubtless for them to= improve), but I am not the person to do that...

Is some= one willing to edit my notes, and post a nice tutorial somewhere?=C2=A0 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.=C2=A0

If not, and in any case, here is the b= ulk 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 s= ervice registry)
#4. Switch to jd= bc auth (QUERY) on CAS - (may need to set permission for guac_username) - c= an test auth-d via cas logon page first
#5. As above, but with Gauth
###########################################################= #########################################################################= =C2=A0

= ##################################################= ###########################################################################= #######
## Install CAS webapp via= the=C2=A0overlay method
# can ch= ange <cas.version> in pom.xml for other versions...
= =C2=A0 mkdir /opt; cd /opt
=C2=A0 c= d cas
=C2=A0 chmod a+x build.sh




#####################################################################= ###############################################################
# To eliminate: "Non-secure Connection&= quot; warning, add secure=3D"true" to 8080 of /var/lib/tomcat8/co= nf/server.xml

### ./etc/cas/config/log4j2.xml: set <Proper= ty name=3D"cas.log.dir">/var/log/tomcat8</Property>
=C2=A0 sed -i -e '/"cas.log.= dir"/ s:>.*<:>/var/log/tomcat8<:' etc/cas/config/log4j= 2.xml
=C2=A0 mkdir -p /etc/cas/lo= gs; chmod a+w /etc/cas/logs

<= /font>
### ./pom.xml - will need this ev= entually=C2=A0
#=C2=A0 =C2=A0 =C2= =A0 =C2=A0<Property name=3D"cas.version">5.2.0-RC4-SNAPSHOT= </Property>

## ./etc/cas/config/cas.properties
## Enable logging...
=C2=A0 logging.level.org.apereo: TRACE
=C2=A0 logging.config: file:/etc/cas/config/log= 4j2.xml

## Set CAS server name URL...
=C2=A0 cas.server.prefix: ${cas.server.name}/cas

## Enable= basic admin pages...
=C2=A0 cas.= adminPagesSecurity.ip=3D172\.27\.0\.99
=C2=A0 cas.monitor.endpoints.enabled=3Dtrue
=C2=A0 cas.monitor.endpoints.sensitive=3Dfalse



#####################################################= ###########################################################################= ####


service t= omcat8 stop
rm=C2=A0 =C2=A0 /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/webap= ps
cp -r etc/cas/ /etc
service tomcat8 restart
tail -f /var/log/tomcat8/catalina.out

=
############################= ###########################################################################= #############################


=
### ./etc/cas/config/cas.properties
## A whitelist of users (use SHA-256 passw= ord hash)...
# cas.authn.accept.u= sers=3Ddbonnes::P@ssw0rd
=C2=A0 c= as.authn.accept.users=3Ddbonnes::d61bcb77d84080738bd59999993b181400992e8c27= 2b372bb4e33522427936
=C2=A0 cas.a= uthn.accept.passwordEncoder.type=3DDEFAULT
=C2=A0 cas.authn.accept.passwordEncoder.characterEncoding=3DUTF-8=
=C2=A0 cas.authn.accept.password= Encoder.encodingAlgorithm=3DSHA-256



######= ###########################################################################= ###################################################
=
###= ./pom.xml
<dependency>
=C2=A0 =C2=A0 <groupId>org.ape= reo.cas</groupId>
=C2=A0 = =C2=A0 <artifactId>cas-server-support-json-service-registry</artif= actId>
=C2=A0 =C2=A0 <versi= on>${cas.version}</version>
</dependency>

### ./etc/cas/config/cas.properties
## Register default services, incl= uding: ^(https|imaps)://.* [ **NOT** http://.* ]
# cas.serviceRegistry.initFromJson=3Dtrue
= ## Register specified services (this is fixed on/b= efore 5.2.0-RC2-SNAPSHOT, but after 5.1.4)
=C2=A0 cas.serviceRegistry.json.location=3Dfile:/etc/cas/services=

### ./etc/cas/services/http-1001.json
=C2=A0 mkdir -p etc/cas/services

=C2=A0 c= at <<EOF > etc/cas/services/http-1001.json
{
=C2=A0 "= ;@class" : "org.apereo.cas.services.RegexRegisteredService",=
=C2=A0 "serviceId" : &= quot;^(https|http)://.*",
= =C2=A0 "name" : "HTTP(S), for testbeds only",
=C2=A0 "id" : 1001,
=
}
<= br>
EOF

## Check vi= a: cat /var/log/tomcat8/cas.log | grep http


=
#############################################= ###########################################################################= ############
## Guac says: *appen= ds* Salt to password, see: https://guacamole.incubator.apa= che.org/doc/gug/jdbc-auth.html
# -- Generate salt
SET @salt = =3D UNHEX(SHA2(UUID(), 256));
# -- Create user and hash passw= ord with salt
INSERT INTO guacamo= le_user (username, password_salt, password_hash)
=C2=A0 =C2=A0 =C2=A0VALUES ('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>
= =C2=A0 =C2=A0 <groupId>org.apereo.cas</gr= oupId>
=C2=A0 =C2=A0 <artif= actId>cas-server-support-jdbc</artifactId>
=C2=A0 =C2=A0 <version>${cas.version}</version&= gt;
</dependency>

<dependency>
=C2=A0 = =C2=A0<groupId>org.apereo.cas</groupId>
=C2=A0 =C2=A0<artifactId>cas-server-support-jdbc-d= rivers</artifactId>
=C2=A0 = =C2=A0<version>${cas.version}</version>
</dependency>

### ./etc/cas/config/c= as.properties [ using QUERY ]
## = Disable demo accounts (i.e. casuser::Mellon)
=C2=A0 cas.authn.accept.users=3D

# Beware: java.s= ql.SQLException: Access denied for user 'guac_username'@'172.27= .0.999' (using password: YES)
## Using JDBC query authentication...
=C2=A0 cas.authn.jdbc.query[0].url=3Djdbc:mysql://lxc-mysql.home:3306= /guacamole_db?useSSL=3Dfalse
=C2= =A0 cas.authn.jdbc.query[0].driverClass=3Dcom.mysql.cj.jdbc.Driver
# cas.authn.jdbc.query[0].dialect=3Dorg.h= ibernate.dialect.MySQL5Dialect
= =C2=A0 cas.authn.jdbc.query[0].user=3Dguacamole_user
=C2=A0 cas.authn.jdbc.query[0].password=3Dguac_Passw0rd=

=C2=A0 cas.authn.jdbc.query[0].sql=3DSELECT * FROM guacamole= _user WHERE username=3D?
=C2=A0 c= as.authn.jdbc.query[0].fieldDisabled=3Ddisabled
=C2=A0 cas.authn.jdbc.query[0].fieldExpired=3Dexpired=
=C2=A0 cas.authn.jdbc.query[0].fieldPas= sword=3Dusername

=C2=A0 cas.authn.jdbc.query[0].passwordEncod= er.type=3DDEFAULT
=C2=A0 cas.auth= n.jdbc.query[0].passwordEncoder.characterEncoding=3DUTF-8
= =C2=A0 cas.authn.jdbc.encode[0].passwordEncoder.en= codingAlgorithm=3DSHA-256



### ./etc/cas/co= nfig/cas.properties [ using ENCODE ]
## Using JDBC encode authentication...
=C2=A0 cas.authn.jdbc.encode[0].url=3Djdbc:mysql://lxc-mysql.ho= me:3306/guacamole_db?useSSL=3Dfalse
=C2=A0 cas.authn.jdbc.encode[0].driverClass=3Dcom.mysql.cj.jdbc.Driver
=C2=A0 cas.authn.jdbc.encode[0].di= alect=3Dorg.hibernate.dialect.MySQL5Dialect
=C2=A0 cas.authn.jdbc.encode[0].user=3Dguacamole_user
=C2=A0 cas.authn.jdbc.encode[0].password=3D= guac_Passw0rd

=C2=A0 cas.authn.jdbc.encode[0].sql=3DSELECT * = FROM guacamole_user WHERE username=3D?
=C2=A0 cas.authn.jdbc.encode[0].fieldDisabled=3Ddisabled
=
=C2=A0 cas.authn.jdbc.encode[0].fieldExpired= =3Dexpired

= # cas.authn.jdbc.encode[0].numberOfIterations=3D1<= /font>
# cas.authn.jdbc.encode[0].static= Salt=3D
=C2=A0 cas.authn.jdbc.enc= ode[0].saltFieldName=3Dpassword_salt
=C2=A0 cas.authn.jdbc.encode[0].passwordFieldName=3Dpassword_hash
=C2=A0 cas.authn.jdbc.encode[0].algori= thmName=3DSHA-256


## Check via: mysql -u guacamole_user --password=3Dguac_Passw0rd -h lxc= -mysql.home (from (say) lxc-tomcat: apt install -y mysql-client)
#=C2=A0 USE guacamole_db;
= #=C2=A0 SELECT username, disabled, expired FROM gu= acamole_user;
#=C2=A0 SELECT user= name, LOWER(hex(password_hash)), hex(password_salt) FROM guacamole_user;

echo -n 'P@ssw0rd' | sha256sum

USE guacam= ole_db;
SET @username =3D "m= yuser2";
SET @password =3D &= quot;P@ssw0rd";
INSERT INTO = guacamole_user (username, password_salt, password_hash, password_date)
=C2=A0 VALUES (@username, NULL, LOWER= (UNHEX(SHA2(@password, 256))), NOW());


= ##################################################= ###########################################################################= #######
#########################= ###########################################################################= ################################
=
### ./pom.xml
<dependency>
=C2=A0 =C2=A0 =C2=A0<groupId>org.apereo.cas</groupI= d>
=C2=A0 =C2=A0 =C2=A0<art= ifactId>cas-server-support-gauth</artifactId>
=C2=A0 =C2=A0 =C2=A0<version>${cas.version}<= /version>
</dependency><= /font>


### ./etc/cas= /services/
# default: failureMode= =3DNOT_SET,bypassEnabled=3Dfalse
= =C2=A0 cat <<EOF > etc/cas/services/http-1001.json
{

=
=C2=A0 "@class" : &quo= t;org.apereo.cas.services.RegexRegisteredService",
=C2=A0 "serviceId" : "^(https|http):/= /.*",
=C2=A0 "name"= ; : "HTTP(S)/MFA, for testbeds only",
=C2=A0 "id" : 1001,
=C2=A0 "multifactorPolicy" : {
=C2=A0 =C2=A0 "@class" : "org.apereo.cas= .services.DefaultRegisteredServiceMultifactorPolicy",
=C2=A0 =C2=A0 "multifactorAuthenticationProv= iders" : [ "java.util.LinkedHashSet", [ "mfa-gauth"= ; ] ],
=C2=A0 =C2=A0 "bypass= Enabled" : false,
=C2=A0 =C2= =A0 "failureMode" : "CLOSED"
=C2=A0 }
}=

EOF


##= # ./etc/cas/config/cas.properties

=C2=A0 cas.authn.mfa.globalFailureMode=3DCLOSED=

= =C2=A0 cas.authn.mfa.gauth.codeDigits=3D6
=C2=A0 cas.authn.mfa.gauth.timeStepSize= =3D30
=C2=A0 cas.authn.mfa.gauth.= trustedDeviceEnabled=3Dtrue
=C2= =A0 cas.authn.mfa.gauth.issuer=3DGuacamole
=C2=A0 cas.authn.mfa.gauth.label=3DZXlabelZX


<= /font>
## Check via: cat /var/log/tomcat= 8/catalina.out | grep gauth

<= /font>


#############= ###########################################################################= ############################################
###############################################################= #####################################################################

### Create the Guacamole DB/user ('%' instead of 'loca= lhost' for remote access) for user accounts/profiles:
= =C2=A0 mysql -u root --password=3D${sql_root_passw= d} <<EOF
# MySQL script to = create JPA DB & user
=C2=A0 C= REATE DATABASE gauth_db;
=C2=A0 C= REATE USER 'gauth_user'@'%' IDENTIFIED BY 'gauth_Passw0= rd';
=C2=A0 GRANT ALL PRIVILE= GES ON gauth_db.* TO 'gauth_user'@'%';
=C2=A0 FLUSH PRIVILEGES;
=C2=A0 quit
EOF

## Check via:=C2=A0 (from (say) lxc-tomcat: apt install -y mysq= l-client)
# mysql -u gauth_user -= -password=3Dgauth_Passw0rd -h lxc-mysql.home
# USE gauth_db;
# SHO= W tables;

<= font face=3D"monospace">
### = ./pom.xml (this needs jdbc DB)
&l= t;dependency>
=C2=A0 =C2=A0 = =C2=A0<groupId>org.apereo.cas</groupId>
=C2=A0 =C2=A0 =C2=A0<artifactId>cas-server-support= -gauth-jpa</artifactId>
=C2= =A0 =C2=A0 =C2=A0<version>${cas.version}</version>
=
</dependency>


=
### ./etc/cas/config/cas.properties
## jpa
=C2=A0 cas.authn.mfa.gauth.jpa.database.url=3Djdbc:mysql://l= xc-mysql.home:3306/gauth_db?useSSL=3Dfalse
=C2=A0 cas.authn.mfa.gauth.jpa.database.driverClass=3Dcom.mysql.c= j.jdbc.Driver
=C2=A0 cas.authn.mf= a.gauth.jpa.database.dialect=3Dorg.hibernate.dialect.MySQL5Dialect
=C2=A0 cas.authn.mfa.gauth.jpa.database.u= ser=3Dgauth_user
=C2=A0 cas.authn= .mfa.gauth.jpa.database.password=3Dgauth_Passw0rd
=C2=A0 cas.authn.mfa.gauth.jpa.database.healthQuery=3DSELE= CT version()

# see: https://stackoverflow.com/questions/42135114= /how-does-exactly-spring-jpa-hibernate-ddl-auto-property-works-in-spring
# cas.authn.m= fa.gauth.jpa.database.ddlAuto=3Dvalidate | update | create | create-drop --= create, then (stop tomcat, then) update?
=C2=A0 cas.authn.mfa.gauth.jpa.database.ddlAuto=3Dupdate

# cas.authn.mfa.gauth.jpa.database.ddlAuto=3Dnone
# cas.authn.mfa.gauth.jpa.database.dataSourceName=3D?= ??



=

###################################################################= #################################################################
##########################################= ###########################################################################= ###############

=
#ENDS

#########################= ###########################################################################= ################################
= ###########################################################################= #########################################################
= ## PreReq: wget -qO- https://api.duosecurity.com/auth/= v2/ping | grep OK
##=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0wget -qO- https://api-2bf290a0.duosecurity.= com/rest/v1/ping | grep OK

### ./pom.xml
&l= t;dependency>
=C2=A0 =C2=A0 = =C2=A0<groupId>org.apereo.cas</groupId>
=C2=A0 =C2=A0 =C2=A0<artifactId>cas-server-support= -duo</artifactId>
=C2=A0 = =C2=A0 =C2=A0<version>${cas.version}</version>
</dependency>

## error: NoC= lassDefFoundError: com/duosecurity/client/Http
## see: https://apereo.gi= thub.io/cas/5.1.x/installation/DuoSecurity-Authentication.html
## You may need to add the following repo= sitories to the WAR overlay...
&l= t;repository>
=C2=A0 =C2=A0 &l= t;id>duo</id>
=C2=A0 =C2= =A0 <url>https://dl.bintray.com/uniconiam/maven</url>
</repository>
<repository>
=C2=A0 =C2=A0 <id>duoclient</id>
=C2=A0 =C2=A0 <url>https://jitpack.io</url>
</repository>


### ./etc/cas/config/cas.properties

##= Activate MFA globally for all, regardless of other settings
=C2=A0 cas.authn.mfa.globalProviderId=3Dmfa-duo=

## Describe the global failure mode in case provider cannot = be reached
# cas.authn.mfa.global= FailureMode=3DPHANTOM

=
## Settings for duo
=C2=A0 cas.authn.mfa.duo[0].id=3Dmfa-duo
# cas.authn.mfa.duo[0].rank=3D0
=
=C2=A0 cas.authn.mfa.duo[0].duoApiHost=3Dapi-2bf29099.= duosecurity.com
=C2=A0 cas.au= thn.mfa.duo[0].duoIntegrationKey=3DDIN8CV999RGLL77VZ
=C2=A0 cas.authn.mfa.duo[0].duoSecretKey=3D23qm5rpZd2hA= gRcR1td8fVJi9ww638p099VPBZ
=C2=A0= cas.authn.mfa.duo[0].duoApplicationKey=3Dfb1c21e199999935b2ab3f999016786
# cas.authn.mfa.duo[0].trustedDevi= ceEnabled=3Dtrue
# cas.authn.mfa.= duo[0].name=3D

<= div># cas.authn.mfa.duo[0].bypass.principalAttribu= teName=3Dbypass|skip
# cas.authn.= mfa.duo[0].bypass.principalAttributeValue=3Dtrue|enabled.+
# cas.authn.mfa.duo[0].bypass.authenticationAttri= buteName=3Dbypass|skip
# cas.auth= n.mfa.duo[0].bypass.authenticationAttributeValue=3Dallowed.+|enabled.+
# cas.authn.mfa.duo[0].bypass.authent= icationHandlerName=3DAcceptUsers.+
# cas.authn.mfa.duo[0].bypass.authenticationMethodName=3DLdapAuthenticati= on.+
# cas.authn.mfa.duo[0].bypas= s.credentialClassType=3DUsernamePassword.+


<= div># cas.authn.mfa.gauth.json.config.location=3Df= ile:/somewhere.json

=C2=A0 cat <<EOF > etc/cas/servi= ces/http-1001.json
{
=
=C2=A0 "@class" : "org.apereo.= cas.services.RegexRegisteredService",
=C2=A0 "serviceId" : "^(https|http)://.*",
=C2=A0 "name" : "App= Secured by CAS and Duo",
= =C2=A0 "id" : 1001,
=C2= =A0 "description" : "HTTP secured with username/password and= Duo MFA protection",
=C2=A0= "attributeReleasePolicy" : {
=C2=A0 =C2=A0 "@class" : "org.apereo.cas.services.Ret= urnAllAttributeReleasePolicy"
=C2=A0 },

=C2=A0 "multifactorPolicy" : {
=C2=A0 =C2=A0 "@class" : "= org.apereo.cas.services.DefaultRegisteredServiceMultifactorPolicy",
=C2=A0 =C2=A0 "multifactorAuth= enticationProviders" : [ "java.util.LinkedHashSet", [ "= mfa-duo" ] ],
=C2=A0 =C2=A0 = "bypassEnabled" : false,
=C2=A0 =C2=A0 "failureMode" : "CLOSED"
=C2=A0 },

=C2=A0 "evaluation= Order" : 100
}
<= div>
EOF





--001a114ec758b5070b055a7856ac--