PrestoDB自定义安全认证

205 阅读2分钟

PrestoDB支持的认证方式有3种(前提都是要打开https服务):

  1. Kerberos
  2. LDAP
  3. Password file 本篇博客下面主要介绍Password file认证的代码逻辑和配置。

1 开发自定义的password authenticator

参考官网提供的信息 prestodb.io/docs/curren…

我们需要实现PasswordAuthenticatorFactory,用于创建PasswordAuthenticator实例

2 认证代码逻辑

2.1 入口 PluginManager扫描插件

        for (File file : listFiles(installedPluginsDir)) {
            if (file.isDirectory()) {
                loadPlugin(file.getAbsolutePath());
            }
        }

        for (String plugin : plugins) {
            loadPlugin(plugin);
        }

查看配置文件中的插件路径

sudo vi /etc/presto/conf/node.properties

plugin.dir=/usr/lib/presto/plugin

2.2 在plugin路径下实现PasswordAuthenticatorFactory

技术: ServiceLoader 当外部程序装配该模块时,通过该jar包META-INF/services/里的配置文件找到具体的实现类名,从而完成模块的注入

private void loadPlugin(URLClassLoader pluginClassLoader)
{
    ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, pluginClassLoader);
    List<Plugin> plugins = ImmutableList.copyOf(serviceLoader);

    if (plugins.isEmpty()) {
        log.warn("No service providers of type %s", Plugin.class.getName());
    }

    for (Plugin plugin : plugins) {
        log.info("Installing %s", plugin.getClass().getName());
        installPlugin(plugin);
    }
}

此处会找到plugin下面所有实现Plugin.class的类

2.3 安装Plugin

public void installPlugin(Plugin plugin)
...
for (PasswordAuthenticatorFactory authenticatorFactory : plugin.getPasswordAuthenticatorFactories()) {
    log.info("Registering password authenticator %s", authenticatorFactory.getName());
    passwordAuthenticatorManager.addPasswordAuthenticatorFactory(authenticatorFactory);
}

2.4 创建PasswordAuthenticator

PasswordAuthenticatorManager

public void loadPasswordAuthenticator()
            throws Exception
    {
        if (!required.get()) {
            return;
        }

        File configFileLocation = CONFIG_FILE.getAbsoluteFile();
        Map<String, String> properties = new HashMap<>(loadProperties(configFileLocation));

        String name = properties.remove(NAME_PROPERTY);
        checkArgument(!isNullOrEmpty(name),
                "Password authenticator configuration %s does not contain %s", configFileLocation, NAME_PROPERTY);

        log.info("-- Loading password authenticator --");

        PasswordAuthenticatorFactory factory = factories.get(name);
        checkState(factory != null, "Password authenticator %s is not registered", name);

        PasswordAuthenticator authenticator = factory.create(ImmutableMap.copyOf(properties));
        this.authenticator.set(requireNonNull(authenticator, "authenticator is null"));

        log.info("-- Loaded password authenticator %s --", name);
    }  

private static final String NAME_PROPERTY = "password-authenticator.name";

读取etc/password-authenticator.properties里面的配置,并从factories里面找到name与NAME_PROPERTY一致的factory并用于创建PasswordAuthenticator authenticator。

3开发和配置

3.1 开发

public class NbFilePasswordAuthenticatorPlugin implements Plugin {

    @Override
    public Iterable<PasswordAuthenticatorFactory> getPasswordAuthenticatorFactories()
    {
        return ImmutableList.<PasswordAuthenticatorFactory>builder()
                .add(new NbFilePasswordAuthenticatorFactory())
                .build();
    }
}
public class NbFilePasswordAuthenticatorFactory implements PasswordAuthenticatorFactory {
    @Override
    public String getName() {
        return "nb-password-authenticator";
    }

    @Override
    public PasswordAuthenticator create(Map<String, String> config) {

        return new NbFilePasswordAuthenticator(config);
    }
}
public class NbFilePasswordAuthenticator implements PasswordAuthenticator {

    private static final Logger log = Logger.getLogger("NbFilePasswordAuthenticator");
    private final Supplier<PasswordStore> passwordStoreSupplier;
    private Map<String, String> config;

    public NbFilePasswordAuthenticator(Map<String, String> config) {
        File configFile = null;
        if (config.containsKey("file.password-file")) {
            String filePath = config.get("file.password-file").trim();
            log.log(Level.INFO, "file.password-file=" + filePath);
            configFile = new File(filePath);
            if (!configFile.exists()) {
                log.log(Level.WARNING, "File %s does not exist" + configFile.getAbsolutePath());
                throw new RuntimeException("File " + configFile.getAbsolutePath() + " does not exist");
            }
        } else {
            String msg = "must has file.password-file in etc/password-authenticator.properties";
            log.log(Level.WARNING, msg);
            throw new RuntimeException(msg);
        }

        // default: 1 day
        int period = 86400;
        if (config.containsKey("file.refresh-period")) {
            period = Integer.parseInt(config.get("file.refresh-period"));
        }

        int cacheMaxSize = 1000;

        File finalConfigFile = configFile;
        passwordStoreSupplier = (Supplier<PasswordStore>) memoizeWithExpiration(
                () -> new PasswordStore(finalConfigFile, cacheMaxSize, null),
                period,
                SECONDS);
    }

    @Override
    public Principal createAuthenticatedPrincipal(String user, String password) {
        if (password == null || password.equals("")) {
            throw new AccessDeniedException("password mustn't be blank!");

        }
        if (user == null || user.equals("")) {
            throw new AccessDeniedException("user mustn't be blank!");

        }

        String[] authInfo = password.split(Keys.AUTH_PASSWORD_SPLIT);
        if (authInfo.length < 2) {
            throw new AccessDeniedException("password must has 'nbdata' !");
        }

        if (!passwordStoreSupplier.get().authenticate(authInfo[0], authInfo[1])) {
            throw new AccessDeniedException("Invalid credentials");
        }

        return new BasicPrincipal(user);
    }
}

3.2 配置

3.2.1 创建plugin

cd /usr/lib/presto/plugin/

sudo mkdir nxb-password-authenticators

复制打包好的jar和依赖到nb-password-authenticators目录里面 (coordinator节点上)

3.2.2 配置etc/password-authenticator.properties

sudo vi /usr/lib/presto/etc/password-authenticator.properties

加入以下内容

password-authenticator.name=nb-password-authenticator

file.password-file=/home/hadoop/xxxxx/password-auth.properties

3.2.3 配置config.properties

sudo vi /etc/presto/conf/config.properties

http-server.authentication.type=PASSWORD

http-server.https.enabled=true

http-server.https.port=8447

http-server.https.keystore.path=/home/hadoop/xxxxx/my_htpasswd/presto_keystore.jks

http-server.https.keystore.key=xxxxxxx

最后重启服务