Skip to content

Support for role based access of HadoopCatalog table listing #1941#1979

Merged
rdblue merged 12 commits into
apache:masterfrom
cccs-jc:master
Jan 12, 2021
Merged

Support for role based access of HadoopCatalog table listing #1941#1979
rdblue merged 12 commits into
apache:masterfrom
cccs-jc:master

Conversation

@cccs-jc
Copy link
Copy Markdown
Contributor

@cccs-jc cccs-jc commented Dec 23, 2020

Modified the implementation of the HadoopCatalog to avoid using the deprecated isDirectory method.

Instead used the getFileStatus() method to get both the fact that a path exists and is a directory in a single call.

Created two methods to determine isTableDir() and isDirectory() which catch any I/O exception which are thrown when a user/client does not have access to certain folders.

http://hadoop.apache.org/docs/stable/api/org/apache/hadoop/fs/FileSystem.html#isDirectory-org.apache.hadoop.fs.Path-

@github-actions github-actions Bot added the core label Dec 23, 2020
Comment thread core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java
try {
return fs.listStatus(metadataPath, TABLE_FILTER).length >= 1;
} catch (IOException e) {
return false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. Returning false is not correct.

Copy link
Copy Markdown
Contributor Author

@cccs-jc cccs-jc Dec 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use the iceberg HadoopCatalog over Azure ABFS, Users use spark to list the tables in the catalog. However not every user has access to all tables on disk (we use file system access control list). When a user cannot access a folder the listStatus function will throw a subclass of IOException.

In my ticket I suggested using a flag to configure the HadoopCatalog to throw errors when unable to list these directories or return false if it is expected that some users will not be able to list all folders/tables.

#1941

The listStatus throws an FileNotFoundException when the path provided does not exists. I will handle that as a "normal" condition, any other IOException I will either catch or not based on a configuration flag. This should handle scenarios where not all user's can access all tables/namespaces

What do you think about that proposal?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to catch FileNotFoundException and return false. What is the exception class that you get when a user can't list path? I would rather use that instead of suppressing any IOException if there is a flag set.

It is also unclear how that suppression is set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added support for a flag. However I have not yet retrieved the value from the configuration properties.

The exception thrown is this one:

https://hadoop.apache.org/docs/r3.2.0/api/org/apache/hadoop/fs/azurebfs/contracts/exceptions/AbfsRestOperationException.html

IOException
AzureBlobFileSystemException
AbfsRestOperationException

Unfortunately the exception thrown is an ABFS specific class.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the boolean be configurable? How are you setting it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Ryan, sorry for the late reply. Ya I'm thinking it could support a flag passed in as a configuration via the org.apache.hadoop.conf.Configuration

I'm just not sure how to pass values into that configuration object. For example there is already a FILE_IO_IMPL supported. How would I add an additional configuration like FILE_IO_IMPL?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getting the config from catalog properties looks good, but I'd move the property constant as I noted above.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is really brittle design. It will only work for one specific store and contains the assumption that the store always returns the string "AuthorizationPermissionMismatch" on an error. If the abfs store ever changed that string everything will break.

  • That abfs exception also includes a status code, 403, which is what should really be used as the signal.
  • Other stores return AccessDeniedException. Has anyone considered filing a hadoop bug asking for translation of rest exceptions with 403s into those? I am reasonably confident nobody is going reject PRs there.

@rdblue
Copy link
Copy Markdown
Contributor

rdblue commented Dec 28, 2020

Thanks for working on this, @cccs-jc. I like that it avoids extra calls to the NameNode.

private final String warehouseLocation;
private final FileSystem fs;
private final FileIO fileIO;
private boolean suppressACLExcpetion = false;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private boolean suppressACLExcpetion = false;
private boolean suppressACLException = false;

} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
if (suppressACLExcpetion) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These exceptions could be thrown be errors that don't have anything to do with ACL so can we try come up with a more generic name for this flag? suppressIOException maybe? I guess it depends on where they will be configured and how.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough.

I'm going to add this configuration like it is done for the FILE_IO_IMPL for example.

@rdblue
Copy link
Copy Markdown
Contributor

rdblue commented Jan 5, 2021

I'm still concerned about adding this without a better check. Adding a flag to turn all IOException instances into a false response seems really brittle. Can we inspect the exception message? Is there a cause that is available? Maybe we can at least check the package of the exception instance?

@cccs-jc
Copy link
Copy Markdown
Contributor Author

cccs-jc commented Jan 5, 2021

sure I could check if it's an azure blob storage exception but that would make it specific to azure..

This is the outline of the listTables method. In the end if a location can't be read it will return NoSuchNamespaceException. If for any reason you cannot list the dir/metadata folder it won't return the table.

So the fact that IOException are not return will not break anything. Worst case users don't see the namespace/tables and hunt down why that is. Maybe logging the error would be helpful while still returning false. My goal here is to not interrupt the execution of listTables if certain user's can't access certain tables. So having a log entry might be useful for the admins/operators.

tables listTables(location)
  if !isDirectory(location) (tries to list "location", if any exception return false)
    throw NoSuchNamespaceException

    for dir in location
      if dir.isFolder()
        if isTableDir(dir) (tries to list "dir/metadata", if any exception return false)
          add to list of tables to return

@rdblue
Copy link
Copy Markdown
Contributor

rdblue commented Jan 5, 2021

Logging the error is definitely a good idea.

It sounds like the main problem is that there isn't a specific error to catch. I think it would be fine if any directory that the user doesn't have access to doesn't show up in the list results. But we do need to ensure that ignoring directories isn't applied to other situations where it isn't reasonable. So if we can inspect the exception from Azure and make this work I think that's the way to go. We can support other errors later as we find them.

@cccs-jc
Copy link
Copy Markdown
Contributor Author

cccs-jc commented Jan 6, 2021

sounds good. I will narrow it to the Azure package and also narrow it to when it is listing a table or namespace only.

@cccs-jc
Copy link
Copy Markdown
Contributor Author

cccs-jc commented Jan 6, 2021

I've checked everywhere the following methods are used

isTableDir
isDirectory
isNamespace

In all cases I can see it makes sense to not throw the exception since these are tests. For example

  createNamespace()
    if isNamespace
      throw AlreadyExistsException
	  
    if it does not exists it will try to create it.
    if a user for some reason can't list the dir it will return false and
    proceed to try to create it, at that point it will fail with a IOException
    showing why (could be restricted access or any other reason)
    
  
  listNamespaces()
    if not isNamespace
      throws NoSuchNamespaceException
  
    then proceeds to list the namespaces under that location, so like listTables it might be a partial list
    
  dropNamespace()
    if can't access it will consider it does not exists and return false (not dropped)
  
  
  loadNamespaceMetadata()
    again if can't access namespace a NoSuchNamespaceException is returned

Nonetheless I will add checks for determining if the exception is an azure blob exception.

cccs-jc added 2 commits January 6, 2021 14:18
Conflicts:
	core/src/main/java/org/apache/iceberg/CatalogProperties.java
	core/src/main/java/org/apache/iceberg/hadoop/HadoopCatalog.java
public static final String HIVE_CLIENT_POOL_SIZE = "clients";
public static final int HIVE_CLIENT_POOL_SIZE_DEFAULT = 2;

public static final String HADOOP_SUPPRESS_IOEXCEPTION = "suppress-io-exception";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is specific to Hadoop, let's move it into HadoopCatalog. I don't think the other catalogs would need this because they do not list using FS operations. A private constant in HadoopCatalog should be fine and we can always expose it later if we need to.


String fileIOImpl = properties.get(CatalogProperties.FILE_IO_IMPL);
this.suppressIOException = Boolean.parseBoolean(properties.get(CatalogProperties.HADOOP_SUPPRESS_IOEXCEPTION));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move this to after fileIO is set? The line above and that line are related so we shouldn't separate them.


private boolean shouldSuppressIOException(IOException ioException) {
String name = ioException.getClass().getName();
return suppressIOException && name.startsWith("org.apache.hadoop.fs.azurebfs");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really prefer if this were more specific and looked at the exception message as well to ensure the error is a permissions or ACL problem.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have fixed the code review suggestions.

I'm detecting the azure blob permission exception using the message (that's all I have available). In that message there is an AzureServiceErrorCode of value "AuthorizationPermissionMismatch" which I use to detect a permission issue.

I agree this is much better error handling since it will let other errors be reported.

I'm thinking the flag should thus be "suppress permission errors". What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me!

return false;
} catch (IOException e) {
if (shouldSuppressIOException(e)) {
LOG.warn("Unalbe to metadata directory {}: {}", metadataPath, e);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: Unalbe -> Unable

return false;
} catch (IOException e) {
if (shouldSuppressIOException(e)) {
LOG.warn("Unalbe to list directory {}: {}", path, e);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same typo.

}

public static final String ENGINE_HIVE_ENABLED = "iceberg.engine.hive.enabled";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this file doesn't need to be touched. Could you remove this whitespace change? It may cause git conflicts.

@rdblue
Copy link
Copy Markdown
Contributor

rdblue commented Jan 8, 2021

@cccs-jc, thanks for updating this! Other than the file that doesn't need to be modified and renaming the config property, I think this is ready to go. Thanks for getting this done!

@cccs-jc
Copy link
Copy Markdown
Contributor Author

cccs-jc commented Jan 8, 2021

Hey Ryan,

Made the change. Should be good to go now. Please have a final spot check.
Cheers

@rdblue rdblue merged commit 01fca3d into apache:master Jan 12, 2021
@rdblue
Copy link
Copy Markdown
Contributor

rdblue commented Jan 12, 2021

Thanks, @cccs-jc! The tests are passing after a restart, so I'll merge this.

@steveloughran
Copy link
Copy Markdown
Contributor

FWIW, HADOOP-15710 first reported this issue in 2018. Someone needs to fix this. Will escalate

XuQianJin-Stars pushed a commit to XuQianJin-Stars/iceberg that referenced this pull request Mar 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants