guacamole-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From David Bonnes <da...@bonnes.name>
Subject How-to - Guacamole with Google Authenticator for 2FA
Date Fri, 29 Sep 2017 22:09:39 GMT
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::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
<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.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} <<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/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
<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.DefaultRegisteredServiceMultifactorPolicy",
    "multifactorAuthenticationProviders" : [ "java.util.LinkedHashSet", [
"mfa-duo" ] ],
    "bypassEnabled" : false,
    "failureMode" : "CLOSED"
  },

  "evaluationOrder" : 100
}

EOF

Mime
View raw message