Merge pull request #1149 from Alfresco/feature/SEARCH-2802_SharedSecretAuth

Feature/search 2802 shared secret auth
This commit is contained in:
Angel Borroy
2021-04-15 15:35:41 +02:00
committed by GitHub
17 changed files with 6070 additions and 2934 deletions

View File

@@ -17,10 +17,10 @@ $ tree generators/app/templates/
│   ├── .env │   ├── .env
│   ├── docker-compose-ce.yml │   ├── docker-compose-ce.yml
│   └── docker-compose-ee.yml │   └── docker-compose-ee.yml
├── latest ├── 7.0
│   ├── .env │   ├── .env
│   ├── docker-compose-ce.yml │   ├── docker-compose-ce.yml
│   └── docker-compose-ee.yml├── empty │   └── docker-compose-ee.yml
├── images ├── images
│   ├── alfresco │   ├── alfresco
│   │   ├── Dockerfile │   │   ├── Dockerfile
@@ -82,7 +82,7 @@ $ yo alfresco-docker-compose
## ACS Version ## ACS Version
Currently supported ACS Versions are `latest`, `6.2` and `6.1` Currently supported ACS Versions are `7.0`, `6.2` and `6.1`
This is the first choice to be selected when the generator is executed. This is the first choice to be selected when the generator is executed.
@@ -90,7 +90,7 @@ This is the first choice to be selected when the generator is executed.
? Which ACS version do you want to use? ? Which ACS version do you want to use?
6.1 6.1
6.2 6.2
latest 7.0
``` ```
## AGS Version ## AGS Version
@@ -107,14 +107,15 @@ If you chose ACS 6.1, a prompt will allow you to use AGS.
When using Community, some different options can be combined: When using Community, some different options can be combined:
* Plain HTTP (http) or TLS/SSL Mutual Authentication (https) for communication between Alfresco and SOLR
* Plain HTTP (http) or HTTPs (https) for Http Web Proxy for HTTP access to services * Plain HTTP (http) or HTTPs (https) for Http Web Proxy for HTTP access to services
* Protect the access to SOLR REST API in the Http WebProxy to forbid direct access to Alfresco Web Proxy port * Protect the access to SOLR REST API in the Http WebProxy to forbid direct access to Alfresco Web Proxy port
* Use SOLR Replication in Master/Slave mode (only when using http) * Use SOLR Replication in Master/Slave mode (only when using http)
* Plain HTTP (http), Shared Secret HTTP (secret) or TLS/SSL Mutual Authentication (https) for communication between Alfresco and SOLR
>> Shared Secret is only available from ACS 7.0.1
``` ```
? Would you like to use Alfresco enterprise or community? community ? Would you like to use Alfresco enterprise or community? community
? Would you like to use HTTP or mTLS for Alfresco-SOLR communication? http ? Would you like to use HTTP, Shared Secret or mTLS for Alfresco-SOLR communication? http
? Would you like to use HTTP or HTTPs for Web Proxy? http ? Would you like to use HTTP or HTTPs for Web Proxy? http
? Would you like to protect the access to SOLR REST API? Yes ? Would you like to protect the access to SOLR REST API? Yes
? Would you like to use a SOLR Replication? No ? Would you like to use a SOLR Replication? No

View File

@@ -21,7 +21,7 @@ module.exports = class extends Generator {
type: 'list', type: 'list',
name: 'acsVersion', name: 'acsVersion',
message: 'Which ACS version do you want to use?', message: 'Which ACS version do you want to use?',
choices: [ '6.1', '6.2', 'latest' ], choices: [ '6.1', '6.2', '7.0' ],
default: 'latest' default: 'latest'
}, },
{ {
@@ -41,8 +41,8 @@ module.exports = class extends Generator {
{ {
type: 'list', type: 'list',
name: 'httpMode', name: 'httpMode',
message: 'Would you like to use HTTP or mTLS for Alfresco-SOLR communication?', message: 'Would you like to use HTTP, Shared Secret or mTLS for Alfresco-SOLR communication?',
choices: [ "http", "https" ], choices: [ "http", "https", "secret" ],
default: 'http' default: 'http'
}, },
{ {
@@ -156,7 +156,7 @@ module.exports = class extends Generator {
// Generate boilerplate from "templates" folder // Generate boilerplate from "templates" folder
writing() { writing() {
// Set base template directory: 6.1, 6.2, latest // Set base template directory: 6.1, 6.2, 7.0
var dockerComposeTemplateDirectory = this.props.acsVersion; var dockerComposeTemplateDirectory = this.props.acsVersion;
// Docker Compose environment variables values // Docker Compose environment variables values
@@ -183,7 +183,7 @@ module.exports = class extends Generator {
'alfresco/alfresco-content-repository-community') : 'alfresco/alfresco-content-repository-community') :
(this.props.ags ? (this.props.ags ?
'quay.io/alfresco/alfresco-governance-repository-enterprise': 'quay.io/alfresco/alfresco-governance-repository-enterprise':
'alfresco/alfresco-content-repository' 'quay.io/alfresco/alfresco-content-repository'
) )
); );
@@ -192,7 +192,7 @@ module.exports = class extends Generator {
(this.props.alfrescoVersion == 'community' ? (this.props.alfrescoVersion == 'community' ?
(this.props.ags ? (this.props.ags ?
'alfresco/alfresco-governance-share-community' : 'alfresco/alfresco-governance-share-community' :
'quay.io/alfresco/alfresco-share') : 'alfresco/alfresco-share') :
(this.props.ags ? (this.props.ags ?
'quay.io/alfresco/alfresco-governance-share-enterprise': 'quay.io/alfresco/alfresco-governance-share-enterprise':
'quay.io/alfresco/alfresco-share' 'quay.io/alfresco/alfresco-share'
@@ -219,7 +219,7 @@ module.exports = class extends Generator {
httpMode: this.props.httpMode, httpMode: this.props.httpMode,
httpWebMode: this.props.httpWebMode, httpWebMode: this.props.httpWebMode,
port: (this.props.httpWebMode == 'http' ? '8080' : '443'), port: (this.props.httpWebMode == 'http' ? '8080' : '443'),
secureComms: (this.props.httpMode == 'http' ? 'none' : 'https'), secureComms: (this.props.httpMode == 'http' ? 'none' : this.props.httpMode),
alfrescoPort: (this.props.httpMode == 'http' ? '8080' : '8443'), alfrescoPort: (this.props.httpMode == 'http' ? '8080' : '8443'),
replication: this.props.replication, replication: this.props.replication,
searchSolrHost: (this.props.replication ? "solr6secondary" : "solr6"), searchSolrHost: (this.props.replication ? "solr6secondary" : "solr6"),

View File

@@ -0,0 +1,13 @@
ALFRESCO_TAG=7.0.0
ALFRESCO_CE_TAG=7.0.0
SHARE_TAG=7.0.0
POSTGRES_TAG=13.1
TRANSFORM_CORE_AIO_TAG=2.3.10
TRANSFORM_ROUTER_TAG=1.3.2
SHARED_FILE_STORE_TAG=0.13.0
ACTIVE_MQ_TAG=5.16.1
DIGITAL_WORKSPACE_TAG=2.1.0-adw
ACS_NGINX_TAG=3.1.1
SEARCH_TAG=latest
ZEPPELIN_TAG=latest
ACA_TAG=2.3.0

View File

@@ -15,7 +15,19 @@ services:
KEYSTORE_PASS: kT9X6oe68t <% } %> KEYSTORE_PASS: kT9X6oe68t <% } %>
COMPRESS_CONTENT: "<%=gzip%>" COMPRESS_CONTENT: "<%=gzip%>"
mem_limit: 1800m mem_limit: 1800m
depends_on:
- postgres
environment: environment:
JAVA_TOOL_OPTIONS: "
-Dencryption.keystore.type=JCEKS
-Dencryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
-Dencryption.keyAlgorithm=DESede
-Dencryption.keystore.location=/usr/local/tomcat/shared/classes/alfresco/extension/keystore/keystore
-Dmetadata-keystore.password=mp6yc0UD9e
-Dmetadata-keystore.aliases=metadata
-Dmetadata-keystore.metadata.password=oKIWzVdEdA
-Dmetadata-keystore.metadata.algorithm=DESede
"
JAVA_OPTS : " JAVA_OPTS : "
-Ddb.driver=org.postgresql.Driver -Ddb.driver=org.postgresql.Driver
-Ddb.username=alfresco -Ddb.username=alfresco
@@ -25,20 +37,19 @@ services:
-Dsolr.port.ssl=8983 -Dsolr.port.ssl=8983
-Dsolr.secureComms=<%=secureComms%> -Dsolr.secureComms=<%=secureComms%>
-Dsolr.baseUrl=/solr -Dsolr.baseUrl=/solr
-Dindex.subsystem.name=solr6 -Dindex.subsystem.name=solr6<% if (httpMode == 'secret') { %>
-Dsolr.sharedSecret=secret<% } %>
-Dshare.host=localhost -Dshare.host=localhost
-Dalfresco.port=8080 -Dalfresco.port=8080
-Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos -Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos
-Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\" -Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\"
-Ddeployment.method=DOCKER_COMPOSE -Ddeployment.method=DOCKER_COMPOSE
-DlocalTransform.core-aio.url=http://transform-core-aio:8090/ -DlocalTransform.core-aio.url=http://transform-core-aio:8090/
-Dalfresco-pdf-renderer.url=http://transform-core-aio:8090/ -Dalfresco-pdf-renderer.url=http://transform-core-aio:8090/
-Djodconverter.url=http://transform-core-aio:8090/ -Djodconverter.url=http://transform-core-aio:8090/
-Dimg.url=http://transform-core-aio:8090/ -Dimg.url=http://transform-core-aio:8090/
-Dtika.url=http://transform-core-aio:8090/ -Dtika.url=http://transform-core-aio:8090/
-Dtransform.misc.url=http://transform-core-aio:8090/ -Dtransform.misc.url=http://transform-core-aio:8090/
-Dcsrf.filter.enabled=false -Dcsrf.filter.enabled=false
-Dalfresco.restApi.basicAuthScheme=true -Dalfresco.restApi.basicAuthScheme=true
-Xms1500m -Xmx1500m -Xms1500m -Xmx1500m
@@ -63,6 +74,8 @@ services:
COMPRESS_CONTENT: "<%=gzip%>" COMPRESS_CONTENT: "<%=gzip%>"
SEARCH_LOG_LEVEL: <%=searchLogLevel%> SEARCH_LOG_LEVEL: <%=searchLogLevel%>
mem_limit: 1200m mem_limit: 1200m
depends_on:
- alfresco
environment: environment:
#Solr needs to know how to register itself with Alfresco #Solr needs to know how to register itself with Alfresco
SOLR_ALFRESCO_HOST: "alfresco" SOLR_ALFRESCO_HOST: "alfresco"
@@ -84,6 +97,9 @@ services:
SOLR_OPTS: " SOLR_OPTS: "
-Dsolr.ssl.checkPeerName=false -Dsolr.ssl.checkPeerName=false
-Dsolr.allow.unsafe.resourceloading=true -Dsolr.allow.unsafe.resourceloading=true
" <% } %> <% if (httpMode == 'secret') { %>
SOLR_OPTS: "
-Dalfresco.secureComms.secret=secret
" <% } %> " <% } %>
ports: ports:
- 8083:8983 <% if (httpMode == 'https') { %> - 8083:8983 <% if (httpMode == 'https') { %>
@@ -106,6 +122,8 @@ services:
MASTER_HOST: solr6 <% } %> MASTER_HOST: solr6 <% } %>
COMPRESS_CONTENT: "<%=gzip%>" COMPRESS_CONTENT: "<%=gzip%>"
mem_limit: 1200m mem_limit: 1200m
depends_on:
- alfresco
environment: environment:
#Solr needs to know how to register itself with Alfresco #Solr needs to know how to register itself with Alfresco
SOLR_ALFRESCO_HOST: "alfresco" SOLR_ALFRESCO_HOST: "alfresco"
@@ -127,6 +145,9 @@ services:
SOLR_OPTS: " SOLR_OPTS: "
-Dsolr.ssl.checkPeerName=false -Dsolr.ssl.checkPeerName=false
-Dsolr.allow.unsafe.resourceloading=true -Dsolr.allow.unsafe.resourceloading=true
" <% } %> <% if (httpMode == 'secret') { %>
SOLR_OPTS: "
-Dalfresco.secureComms.secret=secret
" <% } %> " <% } %>
ports: ports:
- 8084:8983 <% if (httpMode == 'https') { %> - 8084:8983 <% if (httpMode == 'https') { %>
@@ -193,6 +214,9 @@ services:
mem_limit: 128m mem_limit: 128m
depends_on: depends_on:
- alfresco - alfresco
- share
- solr6
- content-app
volumes: volumes:
- ./config/nginx.conf:/etc/nginx/nginx.conf - ./config/nginx.conf:/etc/nginx/nginx.conf
- ./config/nginx.htpasswd:/etc/nginx/conf.d/nginx.htpasswd <% if (httpWebMode == 'https') { %> - ./config/nginx.htpasswd:/etc/nginx/conf.d/nginx.htpasswd <% if (httpWebMode == 'https') { %>

View File

@@ -16,14 +16,15 @@ services:
mem_limit: 1800m mem_limit: 1800m
environment: environment:
JAVA_TOOL_OPTIONS: " JAVA_TOOL_OPTIONS: "
-Dencryption.keystore.type=JCEKS
-Dencryption.cipherAlgorithm=DESede/CBC/PKCS5Padding
-Dencryption.keyAlgorithm=DESede
-Dencryption.keystore.location=/usr/local/tomcat/shared/classes/alfresco/extension/keystore/keystore
-Dmetadata-keystore.password=mp6yc0UD9e -Dmetadata-keystore.password=mp6yc0UD9e
-Dmetadata-keystore.aliases=metadata -Dmetadata-keystore.aliases=metadata
-Dmetadata-keystore.metadata.password=mp6yc0UD9e -Dmetadata-keystore.metadata.password=oKIWzVdEdA
-Dmetadata-keystore.metadata.algorithm=AES -Dmetadata-keystore.metadata.algorithm=DESede
<% if (httpMode == 'https') { %> <% if (httpMode == 'https') { %>
-Dencryption.keystore.type=pkcs12
-Dencryption.cipherAlgorithm=AES/CBC/PKCS5Padding
-Dencryption.keyAlgorithm=AES
-Dssl-keystore.password=kT9X6oe68t -Dssl-keystore.password=kT9X6oe68t
-Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo -Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo
-Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t -Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t
@@ -43,12 +44,11 @@ services:
-Dsolr.secureComms=<%=secureComms%> -Dsolr.secureComms=<%=secureComms%>
-Dsolr.baseUrl=/solr <% if (sharding == 'true') { %> -Dsolr.baseUrl=/solr <% if (sharding == 'true') { %>
-Dsolr.useDynamicShardRegistration=true <% } %> -Dsolr.useDynamicShardRegistration=true <% } %>
-Dindex.subsystem.name=solr6 -Dindex.subsystem.name=solr6<% if (httpMode == 'secret') { %>
-Dsolr.sharedSecret=secret<% } %>
-Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos -Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos
-Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\" -Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\"
-Ddeployment.method=DOCKER_COMPOSE -Ddeployment.method=DOCKER_COMPOSE
-Dtransform.service.enabled=true -Dtransform.service.enabled=true
-Dtransform.service.url=http://transform-router:8095 -Dtransform.service.url=http://transform-router:8095
-Dsfs.url=http://shared-file-store:8099/ -Dsfs.url=http://shared-file-store:8099/
@@ -58,7 +58,6 @@ services:
-Dimg.url=http://transform-core-aio:8090/ -Dimg.url=http://transform-core-aio:8090/
-Dtika.url=http://transform-core-aio:8090/ -Dtika.url=http://transform-core-aio:8090/
-Dtransform.misc.url=http://transform-core-aio:8090/ -Dtransform.misc.url=http://transform-core-aio:8090/
-Dcsrf.filter.enabled=false -Dcsrf.filter.enabled=false
-Dalfresco.restApi.basicAuthScheme=true -Dalfresco.restApi.basicAuthScheme=true
-Xms1500m -Xmx1500m -Xms1500m -Xmx1500m
@@ -104,38 +103,28 @@ services:
#Create the default alfresco and archive cores #Create the default alfresco and archive cores
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
SOLR_JAVA_MEM: "-Xms1g -Xmx1g" <% if (httpMode == 'https') { %> SOLR_JAVA_MEM: "-Xms1g -Xmx1g" <% if (httpMode == 'https') { %>
SOLR_SSL_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.truststore" SOLR_SSL_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl.repo.client.truststore"
SOLR_SSL_TRUST_STORE_PASSWORD: "kT9X6oe68t"
SOLR_SSL_TRUST_STORE_TYPE: "JCEKS" SOLR_SSL_TRUST_STORE_TYPE: "JCEKS"
SOLR_SSL_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore" SOLR_SSL_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl.repo.client.keystore"
SOLR_SSL_KEY_STORE_PASSWORD: "kT9X6oe68t"
SOLR_SSL_KEY_STORE_TYPE: "JCEKS" SOLR_SSL_KEY_STORE_TYPE: "JCEKS"
SOLR_SSL_NEED_CLIENT_AUTH: "true" <% if (sharding == 'true') { %> SOLR_SSL_NEED_CLIENT_AUTH: "true"
SOLR_SSL_CLIENT_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore"
SOLR_SSL_CLIENT_KEY_STORE_TYPE: "JCEKS"
SOLR_SSL_CLIENT_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore"
SOLR_SSL_CLIENT_TRUST_STORE_TYPE: "JCEKS" <% } %>
JAVA_TOOL_OPTIONS: "
-Dsolr.jetty.truststore.password=kT9X6oe68t
-Dsolr.jetty.keystore.password=kT9X6oe68t <% if (sharding == 'true') { %>
-Djavax.net.ssl.keyStorePassword=kT9X6oe68t
-Djavax.net.ssl.trustStorePassword=kT9X6oe68t <% } %>
-Dssl-keystore.password=kT9X6oe68t
-Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo-client
-Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t
-Dssl-keystore.ssl-repo-client.password=kT9X6oe68t
-Dssl-truststore.password=kT9X6oe68t
-Dssl-truststore.aliases=ssl-alfresco-ca,ssl-repo,ssl-repo-client
-Dssl-truststore.ssl-alfresco-ca.password=kT9X6oe68t
-Dssl-truststore.ssl-repo.password=kT9X6oe68t
-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t
"
SOLR_OPTS: " SOLR_OPTS: "
-Dsolr.ssl.checkPeerName=false -Dsolr.ssl.checkPeerName=false
-Dsolr.allow.unsafe.resourceloading=true -Dsolr.allow.unsafe.resourceloading=true
" <% } %> <% if (httpMode == 'secret') { %>
SOLR_OPTS: "
-Dalfresco.secureComms.secret=secret
" <% } %> " <% } %>
ports: ports:
- 8083:8983 <% if (httpMode == 'https') { %> - 8083:8983 <% if (httpMode == 'https') { %>
volumes: volumes:
- ./keystores/solr:/opt/<%=searchPath%>/keystore <% } %> - ./keystores/solr:/opt/<%=searchPath%>/keystore <% } %>
ports:
- 8083:8983 <% if (httpMode == 'https') { %>
volumes:
- ./keystores/solr:/opt/<%=searchPath%>/keystore <% } %>
<% if (sharding == 'true' || replication) { %> <% if (sharding == 'true' || replication) { %>
solr6secondary: solr6secondary:
@@ -169,39 +158,29 @@ services:
SOLR_ALFRESCO_PORT: "<%=alfrescoPort%>" SOLR_ALFRESCO_PORT: "<%=alfrescoPort%>"
ALFRESCO_SECURE_COMMS: <%=secureComms%> ALFRESCO_SECURE_COMMS: <%=secureComms%>
#Alfresco needs to know how to call solr #Alfresco needs to know how to call solr
SOLR_SOLR_HOST: "solr6secondary" SOLR_SOLR_HOST: "solr6"
SOLR_SOLR_PORT: "8983" SOLR_SOLR_PORT: "8983"
#Create the default alfresco and archive cores #Create the default alfresco and archive cores
SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive" SOLR_CREATE_ALFRESCO_DEFAULTS: "alfresco,archive"
SOLR_JAVA_MEM: "-Xms1g -Xmx1g" <% if (httpMode == 'https') { %> SOLR_JAVA_MEM: "-Xms1g -Xmx1g" <% if (httpMode == 'https') { %>
SOLR_SSL_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.truststore" SOLR_SSL_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl.repo.client.truststore"
SOLR_SSL_TRUST_STORE_PASSWORD: "kT9X6oe68t"
SOLR_SSL_TRUST_STORE_TYPE: "JCEKS" SOLR_SSL_TRUST_STORE_TYPE: "JCEKS"
SOLR_SSL_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore" SOLR_SSL_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl.repo.client.keystore"
SOLR_SSL_KEY_STORE_PASSWORD: "kT9X6oe68t"
SOLR_SSL_KEY_STORE_TYPE: "JCEKS" SOLR_SSL_KEY_STORE_TYPE: "JCEKS"
SOLR_SSL_NEED_CLIENT_AUTH: "true" <% if (sharding == 'true') { %> SOLR_SSL_NEED_CLIENT_AUTH: "true"
SOLR_SSL_CLIENT_KEY_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore"
SOLR_SSL_CLIENT_KEY_STORE_TYPE: "JCEKS"
SOLR_SSL_CLIENT_TRUST_STORE: "/opt/<%=searchPath%>/keystore/ssl-repo-client.keystore"
SOLR_SSL_CLIENT_TRUST_STORE_TYPE: "JCEKS" <% } %>
JAVA_TOOL_OPTIONS: "
-Dsolr.jetty.truststore.password=kT9X6oe68t
-Dsolr.jetty.keystore.password=kT9X6oe68t <% if (sharding == 'true') { %>
-Djavax.net.ssl.keyStorePassword=kT9X6oe68t
-Djavax.net.ssl.trustStorePassword=kT9X6oe68t <% } %>
-Dssl-keystore.password=kT9X6oe68t
-Dssl-keystore.aliases=ssl-alfresco-ca,ssl-repo-client
-Dssl-keystore.ssl-alfresco-ca.password=kT9X6oe68t
-Dssl-keystore.ssl-repo-client.password=kT9X6oe68t
-Dssl-truststore.password=kT9X6oe68t
-Dssl-truststore.aliases=ssl-alfresco-ca,ssl-repo,ssl-repo-client
-Dssl-truststore.ssl-alfresco-ca.password=kT9X6oe68t
-Dssl-truststore.ssl-repo.password=kT9X6oe68t
-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t
"
SOLR_OPTS: " SOLR_OPTS: "
-Dsolr.ssl.checkPeerName=false -Dsolr.ssl.checkPeerName=false
-Dsolr.allow.unsafe.resourceloading=true -Dsolr.allow.unsafe.resourceloading=true
" <% } %> <% if (httpMode == 'secret') { %>
SOLR_OPTS: "
-Dalfresco.secureComms.secret=secret
" <% } %> " <% } %>
ports:
- 8083:8983 <% if (httpMode == 'https') { %>
volumes:
- ./keystores/solr:/opt/<%=searchPath%>/keystore <% } %>
ports: ports:
- 8084:8983 <% if (httpMode == 'https') { %> - 8084:8983 <% if (httpMode == 'https') { %>
volumes: volumes:

View File

@@ -21,6 +21,9 @@ ENV ALFRESCO_COMMS $ALFRESCO_COMMS
RUN if [ "$ALFRESCO_COMMS" == "https" ] ; then \ RUN if [ "$ALFRESCO_COMMS" == "https" ] ; then \
sed -i '/^bash.*/i sed -i "'"s/alfresco.secureComms=none/alfresco.secureComms=https/g"'" ${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \ sed -i '/^bash.*/i sed -i "'"s/alfresco.secureComms=none/alfresco.secureComms=https/g"'" ${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \
${DIST_DIR}/solr/bin/search_config_setup.sh; \ ${DIST_DIR}/solr/bin/search_config_setup.sh; \
elif [ "$ALFRESCO_COMMS" == "secret" ] ; then \
sed -i '/^bash.*/i sed -i "'"s/alfresco.secureComms=https/alfresco.secureComms=secret/g"'" ${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \
${DIST_DIR}/solr/bin/search_config_setup.sh; \
else \ else \
sed -i '/^bash.*/i sed -i "'"s/alfresco.secureComms=https/alfresco.secureComms=none/g"'" ${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \ sed -i '/^bash.*/i sed -i "'"s/alfresco.secureComms=https/alfresco.secureComms=none/g"'" ${DIST_DIR}/solrhome/templates/rerank/conf/solrcore.properties\n' \
${DIST_DIR}/solr/bin/search_config_setup.sh; \ ${DIST_DIR}/solr/bin/search_config_setup.sh; \

View File

@@ -1,14 +0,0 @@
ALFRESCO_TAG=6.3.0-A10
ALFRESCO_CE_TAG=latest
SHARE_TAG=latest
POSTGRES_TAG=11.4
TRANSFORM_ROUTER_TAG=1.2.0
TRANSFORM_CORE_AIO_TAG=2.2.1
SHARED_FILE_STORE_TAG=0.7.0
ACTIVE_MQ_TAG=5.15.8
DIGITAL_WORKSPACE_TAG=1.5.0
ACS_NGINX_TAG=3.0.1
ACS_COMMUNITY_NGINX_TAG=1.0.0
SEARCH_TAG=latest
ZEPPELIN_TAG=latest
ACA_TAG=master-latest

File diff suppressed because it is too large Load Diff

View File

@@ -21,10 +21,9 @@
"npm": ">= 4.0.0" "npm": ">= 4.0.0"
}, },
"dependencies": { "dependencies": {
"chalk": "^2.1.0", "chalk": "^2.4.2",
"yeoman-generator": "^2.0.1", "yeoman-generator": "^4.12.0",
"yo": "^3.1.1", "yosay": "^2.0.2"
"yosay": "^2.0.1"
}, },
"jest": { "jest": {
"testEnvironment": "node" "testEnvironment": "node"

View File

@@ -30,6 +30,7 @@ JAVA_OPTS = ('-Ddb.driver=org.postgresql.Driver -Ddb.username=alfresco -Ddb.pass
'-Dsolr.http.connection.timeout=3000 ') '-Dsolr.http.connection.timeout=3000 ')
MTLS_OPTS = ('-Dsolr.port.ssl=8983 -Dsolr.secureComms=https ') MTLS_OPTS = ('-Dsolr.port.ssl=8983 -Dsolr.secureComms=https ')
HTTP_OPTS = ('-Dsolr.secureComms=none') HTTP_OPTS = ('-Dsolr.secureComms=none')
SECRET_OPTS = ('-Dsolr.secureComms=secret -Dsolr.sharedSecret=secret')
JAVA_TOOL_OPTIONS = ('-Dencryption.keystore.type=JCEKS ' JAVA_TOOL_OPTIONS = ('-Dencryption.keystore.type=JCEKS '
'-Dencryption.cipherAlgorithm=DESede/CBC/PKCS5Padding ' '-Dencryption.cipherAlgorithm=DESede/CBC/PKCS5Padding '
'-Dencryption.keyAlgorithm=DESede ' '-Dencryption.keyAlgorithm=DESede '
@@ -48,7 +49,7 @@ MTLS_JAVA_TOOL_OPTIONS = ('-Dencryption.keystore.type=pkcs12 -Dencryption.cipher
'-Dssl-truststore.alfresco-ca.password=kT9X6oe68t ' '-Dssl-truststore.alfresco-ca.password=kT9X6oe68t '
'-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t') '-Dssl-truststore.ssl-repo-client.password=kT9X6oe68t')
def getJavaOpts(includeAMQ, includeTransform, includeShare, solrHost, solrBaseUrl, sharding, mtls): def getJavaOpts(includeAMQ, includeTransform, includeShare, solrHost, solrBaseUrl, sharding, communication):
solrHost = '-Dsolr.host=' + solrHost solrHost = '-Dsolr.host=' + solrHost
shardingOpts = (SHARDING_OPTS if sharding != None else '') shardingOpts = (SHARDING_OPTS if sharding != None else '')
@@ -56,13 +57,18 @@ def getJavaOpts(includeAMQ, includeTransform, includeShare, solrHost, solrBaseUr
transformOpts = (TRANSFORM_OPTS if includeTransform else '') transformOpts = (TRANSFORM_OPTS if includeTransform else '')
solrBaseUrlOpts = '-Dsolr.baseUrl=' + solrBaseUrl solrBaseUrlOpts = '-Dsolr.baseUrl=' + solrBaseUrl
shareTransformOpts = (SHARE_TRANSFORM_OPTS if includeShare and includeTransform else '') shareTransformOpts = (SHARE_TRANSFORM_OPTS if includeShare and includeTransform else '')
mtlsOpts = (MTLS_OPTS if mtls else HTTP_OPTS) if communication == 'mtls':
commOpts = MTLS_OPTS
elif communication == 'none':
commOpts = HTTP_OPTS
else :
commOpts = SECRET_OPTS
return ' '.join([JAVA_OPTS, amqOpts, transformOpts, shareTransformOpts, solrHost, solrBaseUrlOpts, shardingOpts, mtlsOpts]) return ' '.join([JAVA_OPTS, amqOpts, transformOpts, shareTransformOpts, solrHost, solrBaseUrlOpts, shardingOpts, commOpts])
def getJavaToolOptions(mtls): def getJavaToolOptions(communication):
mtlsJavaToolOptions = (MTLS_JAVA_TOOL_OPTIONS if mtls else '') mtlsJavaToolOptions = (MTLS_JAVA_TOOL_OPTIONS if communication == 'mtls' else '')
return ' '.join([JAVA_TOOL_OPTIONS, mtlsJavaToolOptions]) return ' '.join([JAVA_TOOL_OPTIONS, mtlsJavaToolOptions])
@@ -108,19 +114,21 @@ def getSolrcoreConfig(sharding, shardId, shardCount, shardRange):
print("SolrConfig for Shard: ", shardId, " : ", solrcoreConfig) print("SolrConfig for Shard: ", shardId, " : ", solrcoreConfig)
return solrcoreConfig return solrcoreConfig
def getSolrcoreReplacements(sharding, mtls): def getSolrcoreReplacements(sharding, communication):
"""Returns a dict of replacements to make in the solrcore.properties file.""" """Returns a dict of replacements to make in the solrcore.properties file."""
solrcoreReplacements = {} solrcoreReplacements = {}
if sharding != None: if sharding != None:
solrcoreReplacements['shard.method=DB_ID'] = 'shard.method={}'.format(sharding) solrcoreReplacements['shard.method=DB_ID'] = 'shard.method={}'.format(sharding)
if mtls: if communication == 'mtls':
solrcoreReplacements['alfresco.secureComms=none'] = 'alfresco.secureComms=https' solrcoreReplacements['alfresco.secureComms=none'] = 'alfresco.secureComms=https'
solrcoreReplacements['alfresco.encryption.ssl.keystore.location=.*'] = 'alfresco.encryption.ssl.keystore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.keystore' solrcoreReplacements['alfresco.encryption.ssl.keystore.location=.*'] = 'alfresco.encryption.ssl.keystore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.keystore'
solrcoreReplacements['alfresco.encryption.ssl.keystore.type=.*'] = 'alfresco.encryption.ssl.keystore.type=JCEKS' solrcoreReplacements['alfresco.encryption.ssl.keystore.type=.*'] = 'alfresco.encryption.ssl.keystore.type=JCEKS'
solrcoreReplacements['alfresco.encryption.ssl.truststore.location=.*'] = 'alfresco.encryption.ssl.truststore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.truststore' solrcoreReplacements['alfresco.encryption.ssl.truststore.location=.*'] = 'alfresco.encryption.ssl.truststore.location=\\\\\\/opt\\\\\\/alfresco-search-services\\\\\\/keystore\\\\\\/ssl-repo-client.truststore'
solrcoreReplacements['alfresco.encryption.ssl.truststore.type=.*'] = 'alfresco.encryption.ssl.truststore.type=JCEKS' solrcoreReplacements['alfresco.encryption.ssl.truststore.type=.*'] = 'alfresco.encryption.ssl.truststore.type=JCEKS'
else: elif communication == 'none':
solrcoreReplacements['alfresco.secureComms=https'] = 'alfresco.secureComms=none' solrcoreReplacements['alfresco.secureComms=https'] = 'alfresco.secureComms=none'
else :
solrcoreReplacements['alfresco.secureComms=https'] = 'alfresco.secureComms=secret'
return solrcoreReplacements return solrcoreReplacements
def addAlfrescoMtlsConfig(alfrescoArgsNode): def addAlfrescoMtlsConfig(alfrescoArgsNode):
@@ -168,11 +176,15 @@ def addSolrVolumes(solrNode):
"""Add route to keystores folder""" """Add route to keystores folder"""
solrNode['volumes'] = ['./keystores/solr:/opt/alfresco-search-services/keystore'] solrNode['volumes'] = ['./keystores/solr:/opt/alfresco-search-services/keystore']
def makeSearchNode(outputDirectory, nodeName, externalPort, params, mtls, extraEnvironmentVars={}, solrcoreConfig=[], solrcoreReplacements={}): def addSharedSecretSolrOpts(solrEnvNode):
"""Add a list of values to add in Docker Compose SOLR_OPTS property for Shared Secret communication."""
solrEnvNode['SOLR_OPTS'] = '-Dalfresco.secureComms.secret=secret'
def makeSearchNode(outputDirectory, nodeName, externalPort, params, communication, extraEnvironmentVars={}, solrcoreConfig=[], solrcoreReplacements={}):
# Create a dictionary for the template replacement. # Create a dictionary for the template replacement.
allParams = dict(params) allParams = dict(params)
allParams['SOLR_HOST'] = nodeName allParams['SOLR_HOST'] = nodeName
allParams['ALFRESCO_PORT'] = 8443 if mtls else 8080 allParams['ALFRESCO_PORT'] = 8443 if communication == 'mtls' else 8080
allParams['EXTERNAL_PORT'] = externalPort allParams['EXTERNAL_PORT'] = externalPort
# Properties to add to solrcore.properties. # Properties to add to solrcore.properties.
allParams['SOLRCORE_PROPERTIES'] = '\\n'.join(solrcoreConfig) allParams['SOLRCORE_PROPERTIES'] = '\\n'.join(solrcoreConfig)
@@ -180,7 +192,13 @@ def makeSearchNode(outputDirectory, nodeName, externalPort, params, mtls, extraE
allParams['SOLRCORE_REPLACEMENTS'] = ' '.join(map(lambda pair: '"{}/{}"'.format(*pair), solrcoreReplacements.items())) allParams['SOLRCORE_REPLACEMENTS'] = ' '.join(map(lambda pair: '"{}/{}"'.format(*pair), solrcoreReplacements.items()))
# mTLS settings # mTLS settings
allParams['ALFRESCO_SECURE_COMMS'] = ('https' if mtls else 'none') if communication == 'mtls':
allParams['ALFRESCO_SECURE_COMMS'] = 'https'
elif communication == 'none':
allParams['ALFRESCO_SECURE_COMMS'] = 'none'
else :
allParams['ALFRESCO_SECURE_COMMS'] = 'secret'
allParams['TRUSTSTORE_TYPE'] = 'JCEKS' allParams['TRUSTSTORE_TYPE'] = 'JCEKS'
allParams['KEYSTORE_TYPE'] = 'JCEKS' allParams['KEYSTORE_TYPE'] = 'JCEKS'
@@ -203,12 +221,16 @@ def makeSearchNode(outputDirectory, nodeName, externalPort, params, mtls, extraE
searchNodeYaml['environment'].update(extraEnvironmentVars) searchNodeYaml['environment'].update(extraEnvironmentVars)
# Add mTLS configuration if required # Add mTLS configuration if required
if mtls: if communication == 'mtls':
addSolrMtlsConfig(searchNodeYaml['environment']) addSolrMtlsConfig(searchNodeYaml['environment'])
addSolrOpts(searchNodeYaml['environment']) addSolrOpts(searchNodeYaml['environment'])
addSolrJavaToolOptions(searchNodeYaml['environment']) addSolrJavaToolOptions(searchNodeYaml['environment'])
addSolrVolumes(searchNodeYaml) addSolrVolumes(searchNodeYaml)
# Add shared secret configuration if required
if communication == 'secret':
addSharedSecretSolrOpts(searchNodeYaml['environment'])
return searchNodeYaml return searchNodeYaml
if __name__ == '__main__': if __name__ == '__main__':
@@ -231,7 +253,8 @@ if __name__ == '__main__':
parser.add_argument('-sl', '--searchLogLevel', default='WARN', help='The log level for search (default WARN)', parser.add_argument('-sl', '--searchLogLevel', default='WARN', help='The log level for search (default WARN)',
choices=['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR']) choices=['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'])
parser.add_argument('-o', '--output', default='.', help='The path of the directory to output to') parser.add_argument('-o', '--output', default='.', help='The path of the directory to output to')
parser.add_argument('-mtls', '--mtls', action='store_true', help='Use mTLS between SOLR and Alfresco Repository') parser.add_argument('-comm', '--communication', default='none', help='Use none, mtls or secret communication between SOLR and Alfresco Repository',
choices=['none', 'mtls', 'secret'])
args = parser.parse_args() args = parser.parse_args()
# If sharding is selected then the default number of shards is two. # If sharding is selected then the default number of shards is two.
@@ -280,20 +303,20 @@ if __name__ == '__main__':
if shardId == 0 and replicationType == 'standalone': if shardId == 0 and replicationType == 'standalone':
serviceName = 'search' serviceName = 'search'
externalPort = 8083 + 100 * shardId + (1 if replicationType == 'slave' else 0) externalPort = 8083 + 100 * shardId + (1 if replicationType == 'slave' else 0)
dcYaml['services'][serviceName] = makeSearchNode(args.output, serviceName, externalPort, params, args.mtls, dcYaml['services'][serviceName] = makeSearchNode(args.output, serviceName, externalPort, params, args.communication,
extraEnvironmentVars=getExtraEnvironmentVars(serviceName, replicationType), extraEnvironmentVars=getExtraEnvironmentVars(serviceName, replicationType),
solrcoreConfig=getSolrcoreConfig(args.sharding, shardId, args.shardCount, args.shardRange), solrcoreConfig=getSolrcoreConfig(args.sharding, shardId, args.shardCount, args.shardRange),
solrcoreReplacements=getSolrcoreReplacements(args.sharding, args.mtls)) solrcoreReplacements=getSolrcoreReplacements(args.sharding, args.communication))
# Point Alfresco at whichever Solr node came last in the list. # Point Alfresco at whichever Solr node came last in the list.
solrHost = serviceName solrHost = serviceName
solrBaseUrl = '/solr-slave' if args.masterslave else '/solr' solrBaseUrl = '/solr-slave' if args.masterslave else '/solr'
javaOpts = getJavaOpts(not args.excludeAMQ, args.transformer == AIO_TRANSFORMERS, args.share != None, solrHost, solrBaseUrl, args.sharding, args.mtls) javaOpts = getJavaOpts(not args.excludeAMQ, args.transformer == AIO_TRANSFORMERS, args.share != None, solrHost, solrBaseUrl, args.sharding, args.communication)
dcYaml['services']['alfresco']['environment']['JAVA_OPTS'] = javaOpts dcYaml['services']['alfresco']['environment']['JAVA_OPTS'] = javaOpts
javaToolOpts = getJavaToolOptions(args.mtls) javaToolOpts = getJavaToolOptions(args.communication)
dcYaml['services']['alfresco']['environment']['JAVA_TOOL_OPTIONS'] = javaToolOpts dcYaml['services']['alfresco']['environment']['JAVA_TOOL_OPTIONS'] = javaToolOpts
if args.mtls: if args.communication == 'mtls':
addAlfrescoMtlsConfig(dcYaml['services']['alfresco']['build']['args']) addAlfrescoMtlsConfig(dcYaml['services']['alfresco']['build']['args'])
addAlfrescoVolumes(dcYaml['services']['alfresco']) addAlfrescoVolumes(dcYaml['services']['alfresco'])
@@ -321,5 +344,5 @@ if __name__ == '__main__':
dockerfileTemplate = f.write(dockerfileString) dockerfileTemplate = f.write(dockerfileString)
# Copy the keystores (when using mTLS) # Copy the keystores (when using mTLS)
if args.mtls: if args.communication == 'mtls':
copy_tree(scriptDir + '/keystores', args.output + '/keystores') copy_tree(scriptDir + '/keystores', args.output + '/keystores')

View File

@@ -45,6 +45,7 @@ import org.alfresco.solr.SolrInformationServer;
import org.alfresco.solr.SolrKeyResourceLoader; import org.alfresco.solr.SolrKeyResourceLoader;
import org.alfresco.solr.client.SOLRAPIClient; import org.alfresco.solr.client.SOLRAPIClient;
import org.alfresco.solr.client.SOLRAPIClientFactory; import org.alfresco.solr.client.SOLRAPIClientFactory;
import org.alfresco.solr.security.SecretSharedPropertyCollector;
import org.alfresco.solr.tracker.AclTracker; import org.alfresco.solr.tracker.AclTracker;
import org.alfresco.solr.tracker.CascadeTracker; import org.alfresco.solr.tracker.CascadeTracker;
import org.alfresco.solr.tracker.CommitTracker; import org.alfresco.solr.tracker.CommitTracker;
@@ -114,6 +115,9 @@ public class SolrCoreLoadListener extends AbstractSolrEventListener
TrackerRegistry trackerRegistry = admin.getTrackerRegistry(); TrackerRegistry trackerRegistry = admin.getTrackerRegistry();
Properties coreProperties = new CoreDescriptorDecorator(core.getCoreDescriptor()).getProperties(); Properties coreProperties = new CoreDescriptorDecorator(core.getCoreDescriptor()).getProperties();
// Add secret shared properties if required, as they are passed as Java Environment Variables
coreProperties = SecretSharedPropertyCollector.completeCoreProperties(coreProperties);
SolrResourceLoader loader = core.getLatestSchema().getResourceLoader(); SolrResourceLoader loader = core.getLatestSchema().getResourceLoader();
SolrKeyResourceLoader keyResourceLoader = new SolrKeyResourceLoader(loader); SolrKeyResourceLoader keyResourceLoader = new SolrKeyResourceLoader(loader);
SOLRAPIClientFactory clientFactory = new SOLRAPIClientFactory(); SOLRAPIClientFactory clientFactory = new SOLRAPIClientFactory();

View File

@@ -0,0 +1,94 @@
/*
* #%L
* Alfresco Search Services
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.solr.security;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.solr.security.AuthenticationPlugin;
/**
* SOLR Authentication Plugin based in shared secret token via request header.
*
* This Web Filter is loaded from SOLR_HOME/security.json file, so it will be executed
* for every request to SOLR. Authentication logic is only applied when Alfresco Communication
* is using "secret" method but it doesn't apply to "none" and "https" methods.
*
*/
public class SecretSharedAuthPlugin extends AuthenticationPlugin
{
/**
* Verify that request header includes "secret" word when using "secret" communication method.
* "alfresco.secureComms.secret" value is expected as Java environment variable.
*/
@Override
public boolean doAuthenticate(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception
{
if (SecretSharedPropertyCollector.isCommsSecretShared())
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (Objects.equals(httpRequest.getHeader(SecretSharedPropertyCollector.getSecretHeader()),
SecretSharedPropertyCollector.getSecret()))
{
chain.doFilter(request, response);
return true;
}
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN,
"Authentication failure: \"" + SecretSharedPropertyCollector.SECRET_SHARED_METHOD_KEY
+ "\" method has been selected, use the right request header with the secret word");
return false;
}
chain.doFilter(request, response);
return true;
}
@Override
public void init(Map<String, Object> parameters)
{
}
@Override
public void close() throws IOException
{
}
}

View File

@@ -0,0 +1,221 @@
/*
* #%L
* Alfresco Search Services
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.solr.security;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.solr.AlfrescoSolrDataModel;
import org.alfresco.solr.config.ConfigUtil;
import org.apache.solr.core.SolrResourceLoader;
/**
* Provides property values for Alfresco Communication using "secret" method:
*
* - "alfresco.secureComms" is the commsMethod (none, https or secret)
* - "alfresco.secureComms.secret" is the word used as shared secret for "secret" method
* - "alfresco.secureComms.secret.header" is the request header name used for "secret" method
*
*/
public class SecretSharedPropertyCollector
{
public final static String SECRET_SHARED_METHOD_KEY = "secret";
// Property names for "secret" communication method
private static String SECURE_COMMS_PROPERTY = "alfresco.secureComms";
private static String SHARED_SECRET = "alfresco.secureComms.secret";
private static String SHARED_SECRET_HEADER = "alfresco.secureComms.secret.header";
// Save communication method as static value in order to improve performance
private static String commsMethod;
/**
* Check if communications method is "secret"
* @return true when communications method is "secret"
*/
public static boolean isCommsSecretShared()
{
return Objects.equals(SecretSharedPropertyCollector.getCommsMethod(),
SecretSharedPropertyCollector.SECRET_SHARED_METHOD_KEY);
}
/**
* Get communication method from environment variables, shared properties or core properties.
* @return Communication method: none, https, secret
*/
private static String getCommsMethod()
{
if (commsMethod == null)
{
// Environment variable
commsMethod = ConfigUtil.locateProperty(SECURE_COMMS_PROPERTY, null);
if (commsMethod == null)
{
// Shared configuration (shared.properties file)
commsMethod = AlfrescoSolrDataModel.getCommonConfig().getProperty(SECURE_COMMS_PROPERTY);
if (commsMethod == null)
{
// Get configuration from deployed SOLR Cores
Set<String> secureCommsSet = getCommsFromCores();
if (secureCommsSet.size() > 1 && secureCommsSet.contains(SECRET_SHARED_METHOD_KEY))
{
throw new RuntimeException(
"No valid secure comms values: all the cores must be using \"secret\" communication method but found: "
+ secureCommsSet);
}
commsMethod = secureCommsSet.stream().findFirst().get();
}
}
}
return commsMethod;
}
/**
* Read different values of "alfresco.secureComms" property from every "solrcore.properties" files.
* @return List of different communication methods declared in SOLR Cores.
*/
private static Set<String> getCommsFromCores()
{
Set<String> secureCommsSet = new HashSet<>();
try (Stream<Path> walk = Files.walk(Paths.get(SolrResourceLoader.locateSolrHome().toString())))
{
List<String> coreFiles = walk.map(x -> x.toString())
.filter(f -> f.contains("solrcore.properties") && !f.contains("templates"))
.collect(Collectors.toList());
coreFiles.forEach(coreFile -> {
Properties props = new Properties();
try
{
props.load(new FileReader(coreFile));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
String prop = props.getProperty(SECURE_COMMS_PROPERTY);
if (prop != null)
{
secureCommsSet.add(prop);
}
else
{
secureCommsSet.add("none");
}
});
}
catch (IOException e)
{
throw new RuntimeException(e);
}
return secureCommsSet;
}
/**
* Read "secret" word from Java environment variable "alfresco.secureComms.secret"
*
* It can be set from command line invocation using default "-D" parameter:
*
* solr start -a "-Dcreate.alfresco.defaults=alfresco -Dalfresco.secureComms.secret=secret"
*
* @return value for the "secret" word
*/
public static String getSecret()
{
String secret = ConfigUtil.locateProperty(SHARED_SECRET, null);
if (secret == null || secret.length() == 0)
{
throw new RuntimeException("Missing value for " + SHARED_SECRET + " configuration property");
}
return secret;
}
/**
* Read secret request header name from Java environment variable "alfresco.secureComms.secret.header".
* If it's not specified, used default value "X-Alfresco-Search-Secret"
*
* @return value for the secret request header
*/
public static String getSecretHeader()
{
String secretHeader = ConfigUtil.locateProperty(SHARED_SECRET_HEADER, null);
if (secretHeader != null)
{
return secretHeader;
}
else
{
return HttpClientFactory.DEFAULT_SHAREDSECRET_HEADER;
}
}
/**
* Add secret shared properties to original core properties read from "solrcore.properties"
* @param properties Read properties from "solrcore.properties"
* @return when "secret" communication method is configured additional properties are set in original parameter
*/
public static Properties completeCoreProperties(Properties properties)
{
if (isCommsSecretShared())
{
properties.setProperty(SECURE_COMMS_PROPERTY, getCommsMethod());
properties.setProperty(SHARED_SECRET, getSecret());
properties.setProperty(SHARED_SECRET_HEADER, getSecretHeader());
}
return properties;
}
}

View File

@@ -0,0 +1,5 @@
{
"authentication" : {
"class": "org.alfresco.solr.security.SecretSharedAuthPlugin"
}
}

View File

@@ -11,7 +11,7 @@
</parent> </parent>
<properties> <properties>
<dependency.alfresco-data-model.version>11.1</dependency.alfresco-data-model.version> <dependency.alfresco-data-model.version>11.8</dependency.alfresco-data-model.version>
<dependency.jackson.version>2.12.2</dependency.jackson.version> <dependency.jackson.version>2.12.2</dependency.jackson.version>
</properties> </properties>

View File

@@ -52,8 +52,13 @@ public class SOLRAPIClientFactory
*/ */
private static Map<String, SOLRAPIClient> clientsPerAlfresco = new HashMap<>(); private static Map<String, SOLRAPIClient> clientsPerAlfresco = new HashMap<>();
// encryption related parameters // http communication related parameters
private String secureCommsType; // "none", "https" private String secureCommsType; // "none", "https", "secret"
// http
private String alfrescoHost;
private int alfrescoPort;
private String baseUrl;
// ssl // ssl
private String sslKeyStoreType; private String sslKeyStoreType;
@@ -64,19 +69,21 @@ public class SOLRAPIClientFactory
private String sslTrustStoreProvider; private String sslTrustStoreProvider;
private String sslTrustStoreLocation; private String sslTrustStoreLocation;
private String sslTrustStorePasswordFileLocation; private String sslTrustStorePasswordFileLocation;
private String alfrescoHost;
private int alfrescoPort;
private int alfrescoPortSSL; private int alfrescoPortSSL;
private String baseUrl;
// http client // secret shared
private String secret;
private String secretHeader;
// http client settings
private int maxTotalConnections = 40; private int maxTotalConnections = 40;
private int maxHostConnections = 40; private int maxHostConnections = 40;
private int socketTimeout = 120000; private int socketTimeout = 120000;
public static void close()
public static void close() { {
for(SOLRAPIClient client : clientsPerAlfresco.values()) { for (SOLRAPIClient client : clientsPerAlfresco.values())
{
client.close(); client.close();
} }
} }
@@ -131,7 +138,8 @@ public class SOLRAPIClientFactory
* @param namespaceDAO allows retrieving and creating Namespace definitions * @param namespaceDAO allows retrieving and creating Namespace definitions
* @return an instance of SOLRAPIClient * @return an instance of SOLRAPIClient
*/ */
public SOLRAPIClient getSOLRAPIClient(Properties props, KeyResourceLoader keyResourceLoader, DictionaryService dictionaryService, NamespaceDAO namespaceDAO) public SOLRAPIClient getSOLRAPIClient(Properties props, KeyResourceLoader keyResourceLoader,
DictionaryService dictionaryService, NamespaceDAO namespaceDAO)
{ {
if (Boolean.parseBoolean(System.getProperty("alfresco.test", "false"))) if (Boolean.parseBoolean(System.getProperty("alfresco.test", "false")))
@@ -150,7 +158,7 @@ public class SOLRAPIClientFactory
baseUrl = props.getProperty("alfresco.baseUrl", "/alfresco"); baseUrl = props.getProperty("alfresco.baseUrl", "/alfresco");
// Load SSL settings only when using HTTPs protocol // Load SSL settings only when using HTTPs protocol
secureCommsType = props.getProperty("alfresco.secureComms", "none"); secureCommsType = props.getProperty("alfresco.secureComms", "none");
if (secureCommsType.equals("https")) if (SecureCommsType.getType(secureCommsType) == SecureCommsType.HTTPS)
{ {
sslKeyStoreType = getProperty(props, "alfresco.encryption.ssl.keystore.type", "JCEKS"); sslKeyStoreType = getProperty(props, "alfresco.encryption.ssl.keystore.type", "JCEKS");
sslKeyStoreProvider = getProperty(props, "alfresco.encryption.ssl.keystore.provider", ""); sslKeyStoreProvider = getProperty(props, "alfresco.encryption.ssl.keystore.provider", "");
@@ -165,6 +173,11 @@ public class SOLRAPIClientFactory
sslTrustStorePasswordFileLocation = getProperty(props, sslTrustStorePasswordFileLocation = getProperty(props,
"alfresco.encryption.ssl.truststore.passwordFileLocation", ""); "alfresco.encryption.ssl.truststore.passwordFileLocation", "");
} }
if (SecureCommsType.getType(secureCommsType) == SecureCommsType.SECRET)
{
secret = getProperty(props, "alfresco.secureComms.secret", "");
secretHeader = getProperty(props, "alfresco.secureComms.secret.header", "");
}
maxTotalConnections = Integer.parseInt(props.getProperty("alfresco.maxTotalConnections", "40")); maxTotalConnections = Integer.parseInt(props.getProperty("alfresco.maxTotalConnections", "40"));
maxHostConnections = Integer.parseInt(props.getProperty("alfresco.maxHostConnections", "40")); maxHostConnections = Integer.parseInt(props.getProperty("alfresco.maxHostConnections", "40"));
socketTimeout = Integer.parseInt(props.getProperty("alfresco.socketTimeout", "60000")); socketTimeout = Integer.parseInt(props.getProperty("alfresco.socketTimeout", "60000"));
@@ -180,25 +193,32 @@ public class SOLRAPIClientFactory
{ {
HttpClientFactory httpClientFactory = null; HttpClientFactory httpClientFactory = null;
if (secureCommsType.equals("https")) if (SecureCommsType.getType(secureCommsType) == SecureCommsType.HTTPS)
{ {
KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store", KeyStoreParameters keyStoreParameters = new KeyStoreParameters("ssl-keystore", "SSL Key Store",
sslKeyStoreType, sslKeyStoreProvider, sslKeyStorePasswordFileLocation, sslKeyStoreLocation); sslKeyStoreType, sslKeyStoreProvider, sslKeyStorePasswordFileLocation, sslKeyStoreLocation);
KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store", KeyStoreParameters trustStoreParameters = new KeyStoreParameters("ssl-truststore", "SSL Trust Store",
sslTrustStoreType, sslTrustStoreProvider, sslTrustStorePasswordFileLocation, sslTrustStoreLocation); sslTrustStoreType, sslTrustStoreProvider, sslTrustStorePasswordFileLocation,
sslTrustStoreLocation);
SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters, SSLEncryptionParameters sslEncryptionParameters = new SSLEncryptionParameters(keyStoreParameters,
trustStoreParameters); trustStoreParameters);
httpClientFactory = new HttpClientFactory(SecureCommsType.getType(secureCommsType), sslEncryptionParameters, httpClientFactory = new HttpClientFactory(SecureCommsType.getType(secureCommsType), sslEncryptionParameters,
keyResourceLoader, null, null, alfrescoHost, alfrescoPort, alfrescoPortSSL, maxTotalConnections, keyResourceLoader, null, null, null, null, alfrescoHost, alfrescoPort, alfrescoPortSSL,
maxHostConnections, socketTimeout); maxTotalConnections, maxHostConnections, socketTimeout);
} }
else else
{ {
httpClientFactory = new PlainHttpClientFactory(alfrescoHost, alfrescoPort, maxTotalConnections, httpClientFactory = new PlainHttpClientFactory(secureCommsType, alfrescoHost, alfrescoPort,
maxHostConnections, socketTimeout); maxTotalConnections, maxHostConnections, socketTimeout);
if (SecureCommsType.getType(secureCommsType) == SecureCommsType.SECRET)
{
httpClientFactory.setSharedSecret(secret);
httpClientFactory.setSharedSecretHeader(secretHeader);
}
} }
AlfrescoHttpClient repoClient = httpClientFactory.getRepoClient(alfrescoHost, alfrescoPortSSL); AlfrescoHttpClient repoClient = httpClientFactory.getRepoClient(alfrescoHost, alfrescoPortSSL);
repoClient.setBaseUrl(baseUrl); repoClient.setBaseUrl(baseUrl);
return repoClient; return repoClient;
@@ -231,13 +251,13 @@ public class SOLRAPIClientFactory
* Local class to avoid loading sslEntryptionParameters for plain http connections. * Local class to avoid loading sslEntryptionParameters for plain http connections.
* *
* @author aborroy * @author aborroy
*
*/ */
class PlainHttpClientFactory extends HttpClientFactory class PlainHttpClientFactory extends HttpClientFactory
{ {
public PlainHttpClientFactory(String host, int port, int maxTotalConnections, int maxHostConnections, int socketTimeout) public PlainHttpClientFactory(String secureCommsType, String host, int port, int maxTotalConnections,
int maxHostConnections, int socketTimeout)
{ {
setSecureCommsType("none"); setSecureCommsType(secureCommsType);
setHost(host); setHost(host);
setPort(port); setPort(port);
setMaxTotalConnections(maxTotalConnections); setMaxTotalConnections(maxTotalConnections);

View File

@@ -28,7 +28,7 @@ xpp3-1.1.4c.jar http://www.extreme.indiana.edu/dist/java-repository/xpp3/license
=== JSON === === JSON ===
json-20201115.jar https://github.com/stleary/JSON-java json-20210307.jar https://github.com/stleary/JSON-java
=== Apache 2.0 === === Apache 2.0 ===