diff --git a/build-images.sh b/build-images.sh index c41c8e9a47..6032b701e7 100755 --- a/build-images.sh +++ b/build-images.sh @@ -80,6 +80,8 @@ case $CLUSTER_TYPE in docker build -f ./supports/tools/docker/multihost/kerberos/Dockerfile-kdc -t cloud-hub.adsw.io/library/ssm-kdc-server:${HADOOP_VERSION} . + docker build -f ./supports/tools/docker/multihost/ldap/Dockerfile-samba -t hub.adsw.io/qa-samba/samba:demo . + docker build -f ./supports/tools/docker/multihost/datanode/Dockerfile-hadoop-datanode -t cloud-hub.adsw.io/library/hadoop-datanode:${HADOOP_VERSION} . docker build -f ./supports/tools/docker/multihost/namenode/Dockerfile-hadoop-namenode -t cloud-hub.adsw.io/library/hadoop-namenode:${HADOOP_VERSION} . diff --git a/conf/smart-default.xml b/conf/smart-default.xml index b95cdacbb6..198d01b2fe 100644 --- a/conf/smart-default.xml +++ b/conf/smart-default.xml @@ -648,4 +648,12 @@ PASSWORD_COMPARE: search user by specified filters and use LDAP password compare operation + + + smart.rest.server.auth.failures.logging.enabled + true + + Whether to enable unsuccessful REST server auth attempts. + + diff --git a/docs/rest-server-auth.md b/docs/rest-server-auth.md index 8e55e6c430..7857197010 100644 --- a/docs/rest-server-auth.md +++ b/docs/rest-server-auth.md @@ -96,14 +96,14 @@ and `smart.rest.server.auth.ldap.user.search.groups` Supported options: -| Name | Default | Description | -|--------------------------------------------------------|--------------|------------------------------------------------------------------------------------------------| -| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object | -| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses | -| smart.rest.server.auth.ldap.user.attributes.membership | memberOf | The group membership attribute of user LDAP object | -| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass | -| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object | -| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate | +| Name | Default | Description | +|--------------------------------------------------------|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------| +| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object | +| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses | +| smart.rest.server.auth.ldap.user.attributes.membership | memberOf (you should provide this value explicitly in smart-site.xml) | The group membership attribute of user LDAP object | +| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass | +| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object | +| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate | ### Group member attribute strategy @@ -120,14 +120,14 @@ and `smart.rest.server.auth.ldap.user.search.groups` Supported options: -| Name | Default | Description | -|-----------------------------------------------------|--------------|------------------------------------------------------------------------------------------------| -| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object | -| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses | -| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass | -| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object | -| smart.rest.server.auth.ldap.group.attributes.member | member | The member attribute of group LDAP object | -| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate | +| Name | Default | Description | +|-----------------------------------------------------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------| +| smart.rest.server.auth.ldap.user.attributes.name | uid | The name attribute of user LDAP object | +| smart.rest.server.auth.ldap.user.object-classes | person | Comma-separated list of LDAP user entry objectClasses | +| smart.rest.server.auth.ldap.group.object-class | groupOfNames | LDAP group entry objectClass | +| smart.rest.server.auth.ldap.group.attributes.name | cn | The name attribute of group LDAP object | +| smart.rest.server.auth.ldap.group.attributes.member | member (you should provide this value explicitly in smart-site.xml) | The member attribute of group LDAP object | +| smart.rest.server.auth.ldap.user.search.groups | - | Comma-separated list of groups the user should belong to in order to successfully authenticate | ### User name attribute strategy diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java index ef6c502c60..3d673217c4 100644 --- a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java +++ b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java @@ -140,4 +140,7 @@ public class ConfigKeys { public static final String SMART_REST_SERVER_LDAP_BIND_PASSWORD = "smart.rest.server.auth.ldap.bind.password"; + + public static final String SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED = + "smart.rest.server.auth.failures.logging.enabled"; } diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java index 476e51747a..32473a5d4d 100644 --- a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java +++ b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java @@ -18,6 +18,7 @@ package org.smartdata.server.config; import org.smartdata.security.SmartPrincipalManager; +import org.smartdata.server.error.AuthenticationFailureListener; import org.smartdata.server.security.SmartPrincipalInitializerFilter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -33,13 +34,16 @@ import java.util.List; import java.util.Set; +import static org.smartdata.server.config.ConfigKeys.SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED; +import static org.smartdata.server.config.ConfigKeys.WEB_SECURITY_ENABLED; + @Configuration public class SecurityConfiguration { private static final String SESSION_COOKIE_NAME = "SSM_SESSIONID"; private static final String API_ENDPOINTS_PATTERN = "/api/**"; @Bean - @ConditionalOnProperty(name = ConfigKeys.WEB_SECURITY_ENABLED, havingValue = "true") + @ConditionalOnProperty(name = WEB_SECURITY_ENABLED, havingValue = "true") public AuthenticationManager authenticationManager( List authenticationProviders) { if (authenticationProviders.isEmpty()) { @@ -50,7 +54,16 @@ public AuthenticationManager authenticationManager( } @Bean - @ConditionalOnProperty(name = ConfigKeys.WEB_SECURITY_ENABLED, havingValue = "true") + @ConditionalOnProperty( + name = {WEB_SECURITY_ENABLED, SMART_REST_SERVER_AUTH_ERRORS_LOGGING_ENABLED}, + havingValue = "true" + ) + public AuthenticationFailureListener authenticationFailureListener() { + return new AuthenticationFailureListener(); + } + + @Bean + @ConditionalOnProperty(name = WEB_SECURITY_ENABLED, havingValue = "true") public SecurityFilterChain securityFilterChain( HttpSecurity http, SmartPrincipalManager principalManager, @@ -73,7 +86,7 @@ public SecurityFilterChain securityFilterChain( @Bean @ConditionalOnProperty( - name = ConfigKeys.WEB_SECURITY_ENABLED, + name = WEB_SECURITY_ENABLED, havingValue = "false", matchIfMissing = true) public SecurityFilterChain disabledSecurityFilterChain(HttpSecurity http) throws Exception { diff --git a/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java b/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java new file mode 100644 index 0000000000..82b9809642 --- /dev/null +++ b/smart-web-server/src/main/java/org/smartdata/server/error/AuthenticationFailureListener.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.smartdata.server.error; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; + +@Slf4j +public class AuthenticationFailureListener implements + ApplicationListener { + + @Override + public void onApplicationEvent(AbstractAuthenticationFailureEvent event) { + log.error("Failed login attempt for {}", + event.getAuthentication().getName(), event.getException()); + } +} diff --git a/supports/tools/docker/README.md b/supports/tools/docker/README.md index c9b302bb0c..90dc56df0d 100644 --- a/supports/tools/docker/README.md +++ b/supports/tools/docker/README.md @@ -50,11 +50,13 @@ cd ./supports/tools/docker Use one of the following credentials to log in to the Web UI -| Login | Password | Type | -|----------------|-----------|----------| -| john | 1234 | static | -| krb_user1@DEMO | krb_pass1 | kerberos | -| krb_user2@DEMO | krb_pass2 | kerberos | +| Login | Password | Type | +|----------------|---------------|----------| +| john | 1234 | static | +| krb_user1@DEMO | krb_pass1 | kerberos | +| krb_user2@DEMO | krb_pass2 | kerberos | +| july | kitty_cat | ldap | +| ben | bens_password | ldap | ### Testing SPNEGO auth diff --git a/supports/tools/docker/multihost/conf/smart-default.xml b/supports/tools/docker/multihost/conf/smart-default.xml index ac73f944c1..1493ea8ed0 100644 --- a/supports/tools/docker/multihost/conf/smart-default.xml +++ b/supports/tools/docker/multihost/conf/smart-default.xml @@ -544,4 +544,115 @@ Whether to enable SSL support for the SSM REST server. + + + smart.rest.server.auth.ldap.enabled + false + + Whether to enable SSM REST server basic LDAP authentication method support. + + + + + smart.rest.server.auth.ldap.search.base + + + Base LDAP distinguished name for search. + + + + + smart.rest.server.auth.ldap.user.search.base + + + Base LDAP distinguished name for user search. + + + + + smart.rest.server.auth.ldap.group.search.base + + + Base LDAP distinguished name for group search. + + + + + smart.rest.server.auth.ldap.user.attributes.name + uid + + The name attribute of user LDAP object. + + + + + smart.rest.server.auth.ldap.user.object-classes + person + + Comma-separated list of LDAP user entry objectClasses. + + + + + smart.rest.server.auth.ldap.user.search.scope + ONE_LEVEL + + The scope of LDAP user search. Possible values: + OBJECT - Search the named object + ONE_LEVEL - Search one level of the named context + SUBTREE - Search the entire subtree rooted at the named object + + + + + smart.rest.server.auth.ldap.group.search.scope + ONE_LEVEL + + The scope of LDAP group search. Possible values: + OBJECT - Search the named object + ONE_LEVEL - Search one level of the named context + SUBTREE - Search the entire subtree rooted at the named object + + + + + smart.rest.server.auth.ldap.user.attributes.password + userPassword + + The password attribute of user LDAP object. + + + + + smart.rest.server.auth.ldap.group.object-class + groupOfNames + + LDAP group entry objectClass. + + + + + smart.rest.server.auth.ldap.group.attributes.name + cn + + The name attribute of group LDAP object. + + + + + smart.rest.server.auth.ldap.auth.type + BIND + + LDAP authentication type. Possible values: + BIND: search user by specified filters and authenticate with found user's DN and provided password + PASSWORD_COMPARE: search user by specified filters and use LDAP password compare operation + + + + smart.rest.server.auth.failures.logging.enabled + true + + Whether to enable unsuccessful REST server auth attempts. + + diff --git a/supports/tools/docker/multihost/conf/smart-site.xml b/supports/tools/docker/multihost/conf/smart-site.xml index 1cad7ef24e..f834968d31 100644 --- a/supports/tools/docker/multihost/conf/smart-site.xml +++ b/supports/tools/docker/multihost/conf/smart-site.xml @@ -91,4 +91,56 @@ smart.rest.server.auth.spnego.principal HTTP/ssm-server.demo@DEMO + + smart.rest.server.auth.ldap.enabled + true + + + smart.rest.server.auth.ldap.url + ldap://samba:389 + + + smart.rest.server.auth.ldap.search.base + dc=ssm,dc=test + + + smart.rest.server.auth.ldap.user.search.base + ou=people,dc=ssm,dc=test + + + smart.rest.server.auth.ldap.group.search.base + ou=groups,dc=ssm,dc=test + + + smart.rest.server.auth.ldap.user.attributes.name + sAMAccountName + + + smart.rest.server.auth.ldap.user.search.scope + SUBTREE + + + smart.rest.server.auth.ldap.group.search.scope + SUBTREE + + + smart.rest.server.auth.ldap.auth.type + BIND + + + smart.rest.server.auth.ldap.bind.user + cn=Administrator,CN=Users,DC=ssm,DC=test + + + smart.rest.server.auth.ldap.bind.password + LZ2ibGAn2H0D0UW3 + + + smart.rest.server.auth.ldap.user.attributes.membership + memberOf + + + smart.rest.server.auth.ldap.user.search.groups + developers + \ No newline at end of file diff --git a/supports/tools/docker/multihost/docker-compose.yaml b/supports/tools/docker/multihost/docker-compose.yaml index b4b3a31919..8f03cc63e0 100644 --- a/supports/tools/docker/multihost/docker-compose.yaml +++ b/supports/tools/docker/multihost/docker-compose.yaml @@ -110,6 +110,20 @@ services: networks: - demo + samba: + image: hub.adsw.io/qa-samba/samba:demo + hostname: samba + container_name: samba + privileged: true + ports: + - "389:389" + environment: + SMB_ADMIN_PASSWORD: "LZ2ibGAn2H0D0UW3" + volumes: + - ./ldap:/opt/ad-scripts + networks: + - demo + networks: demo: name: demo diff --git a/supports/tools/docker/multihost/ldap/Dockerfile-samba b/supports/tools/docker/multihost/ldap/Dockerfile-samba new file mode 100644 index 0000000000..f9f0b0eb5f --- /dev/null +++ b/supports/tools/docker/multihost/ldap/Dockerfile-samba @@ -0,0 +1,4 @@ +FROM hub.adsw.io/library/adcc-test-ubuntu-base:latest-x64 +RUN mkdir -p /opt/ad-scripts +WORKDIR /opt/ad-scripts +CMD chmod +x *.sh && ./samba-ad-setup.sh && ./samba-ad-run.sh diff --git a/supports/tools/docker/multihost/ldap/people.ldif b/supports/tools/docker/multihost/ldap/people.ldif new file mode 100644 index 0000000000..94921233a1 --- /dev/null +++ b/supports/tools/docker/multihost/ldap/people.ldif @@ -0,0 +1,61 @@ +dn: ou=groups,dc=ssm,dc=test +objectclass: top +objectclass: organizationalUnit +ou: groups + +dn: ou=people,dc=ssm,dc=test +objectclass: top +objectclass: organizationalUnit +ou: people + +dn: cn=july,ou=people,dc=ssm,dc=test +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +cn: july +sn: july +givenName: july +description: kitty_cat pwd +unicodePwd::IgBrAGkAdAB0AHkAXwBjAGEAdAAiAA== +userAccountControl: 512 +sAMAccountName: july + +dn: cn=ben,ou=people,dc=ssm,dc=test +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +cn: ben +sn: ben +givenName: ben +description: bens_password pwd +unicodePwd::IgBiAGUAbgBzAF8AcABhAHMAcwB3AG8AcgBkACIA +userAccountControl: 512 +sAMAccountName: ben + +dn: cn=bob,ou=people,dc=ssm,dc=test +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +cn: bob +sn: bob +givenName: bob +description: b0bs_p4ssw0rd pwd +unicodePwd::IgBiADAAYgBzAF8AcAA0AHMAcwB3ADAAcgBkACIA +userAccountControl: 512 +sAMAccountName: bob + +dn: cn=developers,ou=groups,dc=ssm,dc=test +objectclass: top +objectclass: groupOfNames +cn: developers +member: cn=july,ou=people,dc=ssm,dc=test +member: cn=ben,ou=people,dc=ssm,dc=test + +dn: cn=managers,ou=groups,dc=ssm,dc=test +objectclass: top +objectclass: groupOfNames +cn: managers +member: cn=bob,ou=people,dc=ssm,dc=test diff --git a/supports/tools/docker/multihost/ldap/samba-ad-run.sh b/supports/tools/docker/multihost/ldap/samba-ad-run.sh new file mode 100755 index 0000000000..1de95fecfb --- /dev/null +++ b/supports/tools/docker/multihost/ldap/samba-ad-run.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -e + +[ -f /var/lib/samba/.setup ] || { + >&2 echo "[ERROR] Samba is not setup yet, which should happen automatically. Look for errors!" + exit 127 +} + +samba -i -s /var/lib/samba/private/smb.conf & + +# Update the password policy settings +samba-tool domain passwordsettings set --complexity=off +samba-tool domain passwordsettings set --min-pwd-length=4 +samba-tool domain passwordsettings set --history-length=0 + +sleep 10 + +set +e +ldapadd -x -H ldap://samba:389 -D "cn=Administrator,CN=Users,DC=ssm,DC=test" -w "$SMB_ADMIN_PASSWORD" -f /opt/ad-scripts/people.ldif +LDAPADD_STATUS=$? +set -e + +if [ $LDAPADD_STATUS -ne 0 ]; then + >&2 echo "[ERROR] ldapadd failed with exit code $LDAPADD_STATUS" +fi + +tail -f /dev/null diff --git a/supports/tools/docker/multihost/ldap/samba-ad-setup.sh b/supports/tools/docker/multihost/ldap/samba-ad-setup.sh new file mode 100755 index 0000000000..666a4a06d3 --- /dev/null +++ b/supports/tools/docker/multihost/ldap/samba-ad-setup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +info () { + echo "[INFO] $@" +} + +info "Running setup" + +[ -f /var/lib/samba/.setup ] && info "Already setup..." && exit 0 + +info "Provisioning domain controller..." + +info "Given admin password: ${SMB_ADMIN_PASSWORD}" + +rm -f /etc/samba/smb.conf + +samba-tool domain provision\ + --server-role=dc\ + --use-rfc2307\ + --dns-backend=SAMBA_INTERNAL\ + --realm=ssm.test\ + --domain=test\ + --adminpass=${SMB_ADMIN_PASSWORD} + +sed -i '/^\[global\]/a client ldap sasl wrapping = sign\nldap server require strong auth = no\n' /etc/samba/smb.conf +sed -i '/^\[global\]/a client ldap sasl wrapping = sign\nldap server require strong auth = no\n' /etc/samba/smb.conf + +cp /etc/samba/smb.conf /var/lib/samba/private/smb.conf +rm -f /etc/samba/smb.conf + +touch /var/lib/samba/.setup