Skip to content

Commit

Permalink
Merge pull request eclipse-ditto#1987 from beyonnex-io/add-mongo-aws-…
Browse files Browse the repository at this point in the history
…iam-support

Integrate AWS IAM Role-based Authentication for MongoDB Atlas
  • Loading branch information
thjaeckle authored Jul 23, 2024
2 parents d5f43d0 + 31e0d04 commit c55c0a0
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 11 deletions.
16 changes: 15 additions & 1 deletion bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
<!-- Keep these version consistent with pekko-persistence-mongodb.version's build.sbt -->
<mongo-java-driver.version>5.1.1</mongo-java-driver.version>

<!-- AWS SDK version needed for MongoDB AWS IAM authentication -->
<awssdk.version>2.26.21</awssdk.version>

<jjwt.version>0.12.5</jjwt.version>
<asm.version>9.2</asm.version>
<qpid-jms-client.version>1.11.0</qpid-jms-client.version>
Expand Down Expand Up @@ -206,7 +209,6 @@
<version>${mongo-java-driver.version}</version>
</dependency>


<!-- Pekko Management -->
<dependency>
<groupId>org.apache.pekko</groupId>
Expand Down Expand Up @@ -803,6 +805,18 @@
<version>${classindex.version}</version>
</dependency>

<!-- ### aws sdk ### -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
<version>${awssdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth</artifactId>
<version>${awssdk.version}</version>
</dependency>

<!-- ### Provided - own artifacts ### -->
<dependency>
<groupId>org.eclipse.ditto</groupId>
Expand Down
2 changes: 1 addition & 1 deletion deployment/helm/ditto/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ description: |
A digital twin is a virtual, cloud based, representation of his real world counterpart
(real world “Things”, e.g. devices like sensors, smart heating, connected cars, smart grids, EV charging stations etc).
type: application
version: 3.5.11 # chart version is effectively set by release-job
version: 3.5.12 # chart version is effectively set by release-job
appVersion: 3.5.10
keywords:
- iot-chart
Expand Down
11 changes: 11 additions & 0 deletions deployment/helm/ditto/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,14 @@ We truncate at 63 chars because some Kubernetes name fields are limited to this
{{- end -}}
{{- end -}}
{{- end -}}

{{/*
Get IAM Role ARN from Values if USE_IAM_ROLE is true
*/}}
{{- define "ditto.iamRoleArn" -}}
{{- if .Values.serviceAccount.isUseAwsIamRole -}}
{{ .Values.serviceAccount.roleArn }}
{{- else -}}
""
{{- end -}}
{{- end -}}
8 changes: 8 additions & 0 deletions deployment/helm/ditto/templates/connectivity-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,14 @@ spec:
{{- if .Values.connectivity.extraEnv }}
{{- toYaml .Values.connectivity.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.serviceAccount.isUseAwsIamRole }}
- name: AWS_ROLE_ARN
value: "{{ .Values.serviceAccount.roleArn }}"
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
{{- end }}
- name: USE_AWS_IAM_ROLE
value: "{{ printf "%t" .Values.serviceAccount.isUseAwsIamRole }}"
ports:
- name: remoting
containerPort: {{ .Values.pekko.remoting.port }}
Expand Down
8 changes: 8 additions & 0 deletions deployment/helm/ditto/templates/policies-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,14 @@ spec:
{{- if .Values.policies.extraEnv }}
{{- toYaml .Values.policies.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.serviceAccount.isUseAwsIamRole }}
- name: AWS_ROLE_ARN
value: "{{ .Values.serviceAccount.roleArn }}"
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
{{- end }}
- name: USE_AWS_IAM_ROLE
value: "{{ printf "%t" .Values.serviceAccount.isUseAwsIamRole }}"
ports:
- name: http
containerPort: 8080
Expand Down
4 changes: 4 additions & 0 deletions deployment/helm/ditto/templates/serviceaccount.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ metadata:
name: {{ template "ditto.serviceAccountName" . }}
labels:
app.kubernetes.io/name: {{ include "ditto.name" . }}
annotations:
{{- if .Values.serviceAccount.isUseAwsIamRole }}
eks.amazonaws.com/role-arn: {{ .Values.serviceAccount.roleArn }}
{{- end }}
{{ include "ditto.labels" . | indent 4 }}
{{- end -}}
8 changes: 8 additions & 0 deletions deployment/helm/ditto/templates/things-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@ spec:
{{- if .Values.things.extraEnv }}
{{- toYaml .Values.things.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.serviceAccount.isUseAwsIamRole }}
- name: AWS_ROLE_ARN
value: "{{ .Values.serviceAccount.roleArn }}"
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
{{- end }}
- name: USE_AWS_IAM_ROLE
value: "{{ printf "%t" .Values.serviceAccount.isUseAwsIamRole }}"
ports:
- name: remoting
containerPort: {{ .Values.pekko.remoting.port }}
Expand Down
8 changes: 8 additions & 0 deletions deployment/helm/ditto/templates/thingssearch-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ spec:
{{- if .Values.thingsSearch.extraEnv }}
{{- toYaml .Values.thingsSearch.extraEnv | nindent 12 }}
{{- end }}
{{- if .Values.serviceAccount.isUseAwsIamRole }}
- name: AWS_ROLE_ARN
value: "{{ .Values.serviceAccount.roleArn }}"
- name: AWS_WEB_IDENTITY_TOKEN_FILE
value: "/var/run/secrets/eks.amazonaws.com/serviceaccount/token"
{{- end }}
- name: USE_AWS_IAM_ROLE
value: "{{ printf "%t" .Values.serviceAccount.isUseAwsIamRole }}"
ports:
- name: remoting
containerPort: {{ .Values.pekko.remoting.port }}
Expand Down
8 changes: 8 additions & 0 deletions deployment/helm/ditto/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ serviceAccount:
# name is the name of the service account to use
# If not set and create is true, a name is generated using the fullname template
name:
# isUseAwsIamRole specifies whether to use an AWS IAM Role for Service Accounts (IRSA).
# Set to true to use IRSA, which allows the pod to assume an IAM role.
# When set to true, ensure that roleArn is specified.
isUseAwsIamRole: false
# roleArn is the Amazon Resource Name (ARN) of the IAM role to be assumed by the service account.
# This is required if isUseAwsIamRole is set to true.
# Example: arn:aws:iam::<account-id>:role/<role-name>
roleArn: ""

rbac:
# enabled controls whether RBAC resources will be created
Expand Down
5 changes: 5 additions & 0 deletions internal/utils/config/src/main/resources/ditto-mongo.conf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ ditto.mongodb {
retryWrites = true
retryWrites = ${?MONGO_DB_RETRY_WRITES}

# Determines whether to use AWS IAM roles for authentication with MongoDB.
# When set to true, MongoDB will use the AWS IAM role credentials.
useAwsIamRole = false
useAwsIamRole = ${?USE_AWS_IAM_ROLE}

// all options in the "extra-options" are simply added to the MongoDB URI
// that way any available URI option may be added, even if not explicitly specified in Ditto's mongo config
extra-uri-options {
Expand Down
8 changes: 8 additions & 0 deletions internal/utils/persistence/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@
<groupId>org.apache.pekko</groupId>
<artifactId>pekko-persistence-query_${scala.version}</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@
import javax.annotation.concurrent.NotThreadSafe;
import javax.net.ssl.SSLContext;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.MongoDbConfig;
import org.reactivestreams.Publisher;

import com.mongodb.ClientSessionOptions;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.ReadConcern;
import com.mongodb.ReadPreference;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import com.mongodb.MongoCredential;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.eclipse.ditto.internal.utils.persistence.mongo.config.MongoDbConfig;
import org.reactivestreams.Publisher;

import com.mongodb.connection.ClusterDescription;
import com.mongodb.connection.ServerDescription;
import com.mongodb.connection.TransportSettings;
Expand All @@ -53,6 +54,9 @@

import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;

/**
* Default implementation of DittoMongoClient.
Expand Down Expand Up @@ -259,6 +263,7 @@ static final class MongoClientWrapperBuilder implements DittoMongoClientBuilder,
@Nullable private ConnectionString connectionString;
private String defaultDatabaseName;
private boolean sslEnabled;
private boolean isUseAwsIamRole;
@Nullable private EventLoopGroup eventLoopGroup;

private MongoClientWrapperBuilder() {
Expand Down Expand Up @@ -307,6 +312,7 @@ static GeneralPropertiesStep newInstance(final MongoDbConfig mongoDbConfig) {

final MongoDbConfig.OptionsConfig optionsConfig = mongoDbConfig.getOptionsConfig();
builder.enableSsl(optionsConfig.isSslEnabled());
builder.useAwsIamRole(optionsConfig.isUseAwsIamRole());
builder.setReadPreference(optionsConfig.readPreference().getMongoReadPreference());
builder.setReadConcern(optionsConfig.readConcern().getMongoReadConcern());
builder.setWriteConcern(optionsConfig.writeConcern());
Expand All @@ -315,6 +321,11 @@ static GeneralPropertiesStep newInstance(final MongoDbConfig mongoDbConfig) {
return builder;
}

public MongoClientWrapperBuilder useAwsIamRole(boolean useAwsIamRole) {
this.isUseAwsIamRole = useAwsIamRole;
return this;
}

@Override
public GeneralPropertiesStep connectionString(final String string) {
connectionString = new ConnectionString(checkNotNull(string, "connection string"));
Expand Down Expand Up @@ -440,6 +451,10 @@ public GeneralPropertiesStep setRetryWrites(final boolean retryWrites) {
public MongoClientWrapper build() {
buildAndApplySslSettings();

if (isUseAwsIamRole) {
applyAwsIamRoleSettings();
}

return new MongoClientWrapper(mongoClientSettingsBuilder.build(), defaultDatabaseName,
dittoMongoClientSettingsBuilder.build(), eventLoopGroup);
}
Expand Down Expand Up @@ -476,6 +491,18 @@ private static SSLContext createAndInitSslContext() throws NoSuchAlgorithmExcept
return result;
}

}
private void applyAwsIamRoleSettings() {
final AwsCredentialsProvider credentialsProvider = getAwsCredentialsProvider();
final AwsCredentials credentials = credentialsProvider.resolveCredentials();
final MongoCredential credential = MongoCredential.createAwsCredential(
credentials.accessKeyId(),
credentials.secretAccessKey().toCharArray()
);
mongoClientSettingsBuilder.credential(credential);
}

private static AwsCredentialsProvider getAwsCredentialsProvider() {
return DefaultCredentialsProvider.create();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public final class DefaultOptionsConfig implements MongoDbConfig.OptionsConfig {
*/
static final String CONFIG_PATH = "options";

private final boolean useAwsIamRole;
private final boolean sslEnabled;
private final ReadPreference readPreference;
private final ReadConcern readConcern;
Expand All @@ -47,6 +48,7 @@ public final class DefaultOptionsConfig implements MongoDbConfig.OptionsConfig {
private final Map<String, Object> extraUriOptions;

private DefaultOptionsConfig(final ScopedConfig config) {
useAwsIamRole = config.getBoolean(OptionsConfigValue.USE_AWS_IAM_ROLE.getConfigPath());
sslEnabled = config.getBoolean(OptionsConfigValue.SSL_ENABLED.getConfigPath());
final var readPreferenceString = config.getString(OptionsConfigValue.READ_PREFERENCE.getConfigPath());
readPreference = ReadPreference.ofReadPreference(readPreferenceString)
Expand Down Expand Up @@ -118,6 +120,11 @@ public boolean isRetryWrites() {
return retryWrites;
}

@Override
public boolean isUseAwsIamRole() {
return useAwsIamRole;
}

@Override
public Map<String, Object> extraUriOptions() {
return extraUriOptions;
Expand All @@ -132,7 +139,7 @@ public boolean equals(final Object o) {
return false;
}
final DefaultOptionsConfig that = (DefaultOptionsConfig) o;
return sslEnabled == that.sslEnabled && retryWrites == that.retryWrites &&
return useAwsIamRole == that.useAwsIamRole && sslEnabled == that.sslEnabled && retryWrites == that.retryWrites &&
readPreference == that.readPreference &&
readConcern == that.readConcern &&
Objects.equals(writeConcern, that.writeConcern) &&
Expand All @@ -141,13 +148,14 @@ public boolean equals(final Object o) {

@Override
public int hashCode() {
return Objects.hash(sslEnabled, readPreference, readConcern, writeConcern, retryWrites, extraUriOptions);
return Objects.hash(useAwsIamRole, sslEnabled, readPreference, readConcern, writeConcern, retryWrites, extraUriOptions);
}

@Override
public String toString() {
return getClass().getSimpleName() + " [" +
"sslEnabled=" + sslEnabled +
"useAwsIamRole=" + useAwsIamRole +
", sslEnabled=" + sslEnabled +
", readPreference=" + readPreference +
", readConcern=" + readConcern +
", writeConcern=" + writeConcern +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ interface OptionsConfig {
*/
boolean isRetryWrites();

/**
* Indicates whether to use AWS IAM role for authentication.
*
* @return {@code true} if IAM role should be used, {@code false} otherwise.
*/
boolean isUseAwsIamRole();

/**
* Gets the extra options to add to the configured MongoDB {@code uri}.
*
Expand Down Expand Up @@ -203,6 +210,11 @@ enum OptionsConfigValue implements KnownConfigValue {
*/
RETRY_WRITES("retryWrites", true),

/**
* Determines whether IAM role should be used for authentication.
*/
USE_AWS_IAM_ROLE("useAwsIamRole", false),

/**
* The extra options to add to the configured MongoDB {@code uri}.
*/
Expand Down

0 comments on commit c55c0a0

Please sign in to comment.