Skip to content

Commit

Permalink
Merge pull request #23 from matt-carr/master
Browse files Browse the repository at this point in the history
Implement workload identity-based token generation
  • Loading branch information
oleewere authored Aug 28, 2024
2 parents 1999621 + 463cec1 commit 729dbd8
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 4 deletions.
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,34 @@ $ gem install fluent-plugin-azurestorage-gen2
</match>
```

- Configuration in a pod using Azure Workload Identity:
```
<match **>
@type azurestorage_gen2
azure_storage_account mystorageabfs
azure_container mycontainer
azure_use_workload_id true
azure_oauth_tenant_id <my tenant id>
azure_oauth_app_id <my app client id>
azure_object_key_format %{path}-%{index}.%{file_extension}
azure_oauth_refresh_interval 3600
time_slice_format %Y%m%d-%H
file_extension log # only used with store_as none
path "/cluster-logs/myfolder/${tag[1]}-#{Socket.gethostname}-%M"
auto_create_container true
store_as gzip
format single_value
<buffer tag,time>
@type file
path /var/log/fluent/azurestorage-buffer
timekey 5m
timekey_wait 0s
timekey_use_utc true
chunk_limit_size 64m
</buffer>
</match>
```

- Configuration outside of VMs with OAuth credentials:
```
<match **>
Expand Down Expand Up @@ -97,6 +125,14 @@ $ gem install fluent-plugin-azurestorage-gen2
Your Azure Storage Account Name. This can be got from Azure Management potal.
This parameter is required when environment variable 'AZURE_STORAGE_ACCOUNT' is not set.

### azure_use_workload_id

Use Azure Workload Identity for authentication. The plugin will use a token generated from the kubernetes OIDC issuer to get an Azure OAuth2 token, which will be used to authenticate with the storage API. Supersedes other authentication types. Requires azure_oauth_tenant_id and azure_oauth_app_id to be set. See https://azure.github.io/azure-workload-identity/docs/introduction.html for implementation details. Default is false.

### azure_federated_token_file_path

The path where the federated token is mounted on the local filesystem. If not specified, defaults to the value of the environment variable `AZURE_FEDERATED_TOKEN_FILE`, or `/var/run/secrets/azure/tokens/azure-identity-token` if the environment variable is not set. Defaults set per Azure Workload Identity documentation.

### azure_storage_access_key (not implemented yet - use msi)

Your Azure Storage Access Key(Primary or Secondary). This also can be got from Azure Management potal. Storage access key authentication is used when this parameter is provided or environment variable 'AZURE_STORAGE_ACCESS_KEY' is set.
Expand All @@ -115,11 +151,11 @@ Azure AD object id is a specific explicit identity to use when authenticating to

### azure_oauth_tenant_id (Preview)

Azure account tenant id from your Azure Directory. Required if OAuth based credential mechanism is used.
Azure account tenant id from your Azure Directory. Required if workload ID or OAuth based credential mechanism is used.

### azure_oauth_app_id (Preview)

OAuth client id that is used for OAuth based authentication. Required if OAuth based credential mechanism is used.
OAuth client id that is used for OAuth based authentication. Required if workload ID or OAuth based credential mechanism is used.

### azure_oauth_secret (Preview)

Expand Down
38 changes: 36 additions & 2 deletions lib/fluent/plugin/out_azurestorage_gen2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def initialize
config_param :path, :string, :default => ""
config_param :azure_storage_account, :string, :default => nil
config_param :azure_storage_access_key, :string, :default => nil, :secret => true
config_param :azure_use_workload_id, :string, :default => false
config_param :azure_federated_token_file_path, :string, :default => nil
config_param :azure_instance_msi, :string, :default => nil
config_param :azure_client_id, :string, :default => nil
config_param :azure_object_id, :string, :default => nil
Expand Down Expand Up @@ -261,17 +263,49 @@ def setup_access_token
end

def acquire_access_token
if !@azure_instance_msi.nil?
if @azure_use_workload_id
acquire_access_token_federated
elsif !@azure_instance_msi.nil?
acquire_access_token_msi
elsif !@azure_oauth_app_id.nil? and !@azure_oauth_secret.nil? and !@azure_oauth_tenant_id.nil?
acquire_access_token_oauth_app
elsif @azure_oauth_use_azure_cli
acquire_access_token_by_az
else
raise Fluent::UnrecoverableError, "Using MSI or 'az cli tool' or simple OAuth 2.0 based authentication parameters (azure_oauth_tenant_id, azure_oauth_app_id, azure_oauth_secret) are required."
raise Fluent::UnrecoverableError, "Using MSI or Workload Identity or 'az cli tool' or simple OAuth 2.0 based authentication parameters (azure_oauth_tenant_id, azure_oauth_app_id, azure_oauth_secret) are required."
end
end

private
def acquire_access_token_federated
token_path = @azure_federated_token_file_path ||= ENV['AZURE_TOKEN_FILE'] ||= "/var/run/secrets/azure/tokens/azure-identity-token"
log.debug "azurestorage_gen2: Reading federated token from #{token_path}"
token = File.read(token_path)
log.debug "azurestorage_gen2: Locally mounted token: #{token}"
params = { :"api-version" => ACCESS_TOKEN_API_VERSION, :resource => "#{@url_storage_resource}"}
headers = {:"Content-Type" => "application/x-www-form-urlencoded"}
content = "grant_type=client_credentials&client_id=#{@azure_oauth_app_id}&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=#{token.chomp}&resource=#{@url_storage_resource}&scope=https://storage.azure.com/.default"
req_opts = {
:params => params,
:body => content,
:headers => headers,
:timeout => @http_timeout_seconds
}
add_proxy_options(req_opts)
request = Typhoeus::Request.new("#{@azure_oauth_identity_authority}/#{@azure_oauth_tenant_id}/oauth2/token", req_opts)

request.on_complete do |response|
if response.success?
data = JSON.parse(response.body)
log.debug "azurestorage_gen2: Token response: #{data}"
@azure_access_token = data["access_token"].chomp
else
raise Fluent::UnrecoverableError, "Failed to acquire access token. #{response.code}: #{response.body}"
end
end
request.run
end

# Referenced from azure doc.
# https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-linux-vm-access-storage#get-an-access-token-and-use-it-to-call-azure-storage
private
Expand Down

0 comments on commit 729dbd8

Please sign in to comment.