diff --git a/dolphinscheduler-api/src/main/resources/META-INF/spring.factories b/dolphinscheduler-api/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..0f07922b83bc --- /dev/null +++ b/dolphinscheduler-api/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +# +# 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. +# + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.apache.dolphinscheduler.common.config.DecryptEnvironmentPostProcessor \ No newline at end of file diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/DecryptEnvironmentPostProcessor.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/DecryptEnvironmentPostProcessor.java new file mode 100644 index 000000000000..22a5d0e7da51 --- /dev/null +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/config/DecryptEnvironmentPostProcessor.java @@ -0,0 +1,45 @@ +/* + * 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.apache.dolphinscheduler.common.config; + +import org.apache.dolphinscheduler.common.utils.EncryptionUtils; + +import java.util.Properties; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.EnvironmentPostProcessor; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertiesPropertySource; + +@Slf4j +public class DecryptEnvironmentPostProcessor implements EnvironmentPostProcessor { + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String datasourcePassword = environment.getProperty("spring.datasource.password"); + if (EncryptionUtils.isEncrypted(datasourcePassword)) { + String passwordEncryptKey = environment.getProperty("spring.datasource.encryption.key"); + String decryptedPassword = EncryptionUtils.decryptPassword(datasourcePassword, passwordEncryptKey); + Properties props = new Properties(); + props.put("spring.datasource.password", decryptedPassword); + environment.getPropertySources().addFirst(new PropertiesPropertySource("decryptedProps", props)); + } + } +} diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/EncryptionUtils.java b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/EncryptionUtils.java index c00cc473f685..27b9df5ded82 100644 --- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/EncryptionUtils.java +++ b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/EncryptionUtils.java @@ -17,14 +17,32 @@ package org.apache.dolphinscheduler.common.utils; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; +import java.nio.charset.Charset; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +import lombok.extern.slf4j.Slf4j; + /** * encryption utils */ +@Slf4j public class EncryptionUtils { + private static final int AES_KEY_LEN = 16; // 128 bit + private static final int ITERATIONS = 130_000; + + public static final String ENC_PREFIX = "ENC('"; + public static final String ENC_SUBFIX = "')"; + private EncryptionUtils() { throw new UnsupportedOperationException("Construct EncryptionUtils"); } @@ -37,4 +55,64 @@ public static String getMd5(String rawStr) { return DigestUtils.md5Hex(null == rawStr ? StringUtils.EMPTY : rawStr); } + public static boolean isEncrypted(String value) { + return StringUtils.isNotEmpty(value) && value.startsWith(ENC_PREFIX) && value.endsWith(ENC_SUBFIX); + } + + private static String encrypt(String strToEncrypt, byte[] key) { + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + final SecretKeySpec secretKey = new SecretKeySpec(key, "AES"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes(Charset.defaultCharset()))); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private static String decrypt(String strToDecrypt, String passwordEncryptKey) { + if (StringUtils.isEmpty(passwordEncryptKey)) { + throw new RuntimeException("No encryption key found in config"); + } + try { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); + final SecretKeySpec secretKey = new SecretKeySpec(Base64.decodeBase64(passwordEncryptKey), "AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return new String(cipher.doFinal(Base64.decodeBase64(strToDecrypt)), Charset.defaultCharset()); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public static String decryptPassword(String value, String passwordEncryptKey) { + return decrypt(value.substring(ENC_PREFIX.length(), value.length() - ENC_SUBFIX.length()), passwordEncryptKey); + } + + private static String normalizeKey(String password) { + try { + byte[] salt = new byte[8]; + new SecureRandom().nextBytes(salt); + PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, AES_KEY_LEN * 8); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + byte[] keyBytes = skf.generateSecret(spec).getEncoded(); + return Base64.encodeBase64String(keyBytes); + } catch (Exception e) { + throw new RuntimeException("Key derivation failed", e); + } + } + private static String colorize(String text) { + return "\u001B[31m" + text + "\u001B[0m"; + } + public static void main(String[] args) { + String out = "Encrypted Password is [%s], Encrypted Key is [%s]"; + if (args.length != 2) { + System.out.println("Usage: sh encrypt-password.sh [plain-password] [plain-key]"); + } else { + String password = args[0]; + String key = args[1]; + String normalizedKey = normalizeKey(key); + System.out.printf((out) + "%n", colorize(encrypt(password, Base64.decodeBase64(normalizedKey))), + colorize(normalizedKey)); + } + } } diff --git a/dolphinscheduler-common/src/main/resources/META-INF/spring.factories b/dolphinscheduler-common/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..0f07922b83bc --- /dev/null +++ b/dolphinscheduler-common/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +# +# 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. +# + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.apache.dolphinscheduler.common.config.DecryptEnvironmentPostProcessor \ No newline at end of file diff --git a/dolphinscheduler-master/src/main/resources/META-INF/spring.factories b/dolphinscheduler-master/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..0f07922b83bc --- /dev/null +++ b/dolphinscheduler-master/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +# +# 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. +# + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.apache.dolphinscheduler.common.config.DecryptEnvironmentPostProcessor \ No newline at end of file diff --git a/dolphinscheduler-standalone-server/src/main/resources/META-INF/spring.factories b/dolphinscheduler-standalone-server/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000000..0f07922b83bc --- /dev/null +++ b/dolphinscheduler-standalone-server/src/main/resources/META-INF/spring.factories @@ -0,0 +1,19 @@ +# +# 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. +# + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.apache.dolphinscheduler.common.config.DecryptEnvironmentPostProcessor \ No newline at end of file diff --git a/dolphinscheduler-tools/src/main/bin/encrypt-password.sh b/dolphinscheduler-tools/src/main/bin/encrypt-password.sh new file mode 100644 index 000000000000..dca3a12b7b20 --- /dev/null +++ b/dolphinscheduler-tools/src/main/bin/encrypt-password.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# +# 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. +# + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + exit 1 +fi + +PASSWORD="$1" +KEY="$2" + +BIN_DIR=$(dirname $0) +DOLPHINSCHEDULER_HOME=${DOLPHINSCHEDULER_HOME:-$(cd ${BIN_DIR}/../..;pwd)} +TOOLS_HOME=$(cd ${BIN_DIR}/..;pwd) + +if [ "$DOCKER" != "true" ]; then + source "$DOLPHINSCHEDULER_HOME/bin/env/dolphinscheduler_env.sh" +fi + +JAVA_OPTS=${JAVA_OPTS:-"-server -Duser.timezone=${SPRING_JACKSON_TIME_ZONE} -Xms4g -Xmx4g -Xmn512m -XX:+PrintGCDetails -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=dump.hprof"} + +$JAVA_HOME/bin/java $JAVA_OPTS \ + -cp "$TOOLS_HOME/conf":"$TOOLS_HOME/libs/*" \ + org.apache.dolphinscheduler.common.utils.EncryptionUtils "$PASSWORD" "$KEY" \ No newline at end of file