├── local
└── config
│ ├── env.properties
│ └── service.properties
├── .gitignore
├── Dockerfile
├── CONTRIBUTING
├── NOTICE
├── monitors
├── log4j2-syslog.xml
└── log4j2.xml
├── src
├── main
│ └── java
│ │ └── com
│ │ └── secretsagent
│ │ ├── Main.java
│ │ ├── SecretsManager.java
│ │ └── SecretsProcessor.java
└── test
│ └── java
│ └── com
│ └── secretsagent
│ ├── SecretsManagerTest.java
│ └── SecretsProcessorTest.java
├── start.sh
├── run.sh
├── README.md
├── pom.xml
└── LICENSE
/local/config/env.properties:
--------------------------------------------------------------------------------
1 | LOG_LEVEL=INFO
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.log
2 | target/
3 | local/secrets
4 | *.iml
5 | .idea/
6 | .DS_Store
7 | ._*
8 |
--------------------------------------------------------------------------------
/local/config/service.properties:
--------------------------------------------------------------------------------
1 | secret.name=valws/lab.main.t.aor.valws.vaw2
2 | region=us-east-1
3 | secrets.filename=/secrets/secrets.properties
4 | is.local.run=true
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 | ARG JAR_FILE
3 | COPY /target/${JAR_FILE} /app/agent/${JAR_FILE}
4 | COPY /target/dependency /app/agent
5 | COPY /monitors /app/monitors
6 | COPY start.sh /app/bin/start.sh
7 | RUN chmod +rwx /app/bin/start.sh
8 | ENTRYPOINT ["/app/bin/start.sh"]
--------------------------------------------------------------------------------
/CONTRIBUTING:
--------------------------------------------------------------------------------
1 | If you would like to contribute code to this project you can do so through
2 | GitHub by forking the repository and sending a pull request.
3 |
4 | Before Comcast merges your code into the project you must sign the [Comcast Contributor License Agreement (CLA)](https://gist.github.com/ComcastOSS/a7b8933dd8e368535378cda25c92d19a).
5 |
6 | If you haven't previously signed a Comcast CLA, you'll automatically be asked to when you open a pull request.
7 | Alternatively, we can send you a PDF that you can sign and scan back to us.
8 | Please create a new GitHub issue to request a PDF version of the CLA.
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2018 Comcast Cable Communications Management, LLC
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
15 | This product includes software developed at Comcast (http://www.comcast.com/).
16 |
--------------------------------------------------------------------------------
/monitors/log4j2-syslog.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | awssecretsagent {"index": "service", "ccds_cluster_title": "${clusterTitle}", "type": "awssecrets-agent", "event": "%d{ISO8601_OFFSET_DATE_TIME_HH} %X{CID:--} %-5p [%t] %c: %m%n"}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/com/secretsagent/Main.java:
--------------------------------------------------------------------------------
1 | package com.secretsagent;
2 |
3 | /**
4 | * Copyright 2020 Comcast Cable Communications Management, LLC
5 | *
6 | * Licensed under the Apache License, Version2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | *
18 | * SPDX-License-Identifier: Apache-2.0
19 | */
20 |
21 | public class Main
22 | {
23 | public static void main(String[] args)
24 | {
25 | SecretsProcessor secretsProcessor = new SecretsProcessor();
26 | secretsProcessor.processSecret();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/monitors/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %X{CID:--} %-5p [%t] %c: %m%n
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #==============================================================================================
4 | # Build the full class-path of all the JARs found
5 | CP=""
6 | for I in /app/agent/*.jar; do
7 | CP="$CP:$I"
8 | done
9 |
10 | #==============================================================================================
11 | # Source environment variables for environment properties
12 | . /app/config/env.properties
13 |
14 | # set defaults for variables not defined
15 | [ -z "$LOG_LEVEL" ] && LOG_LEVEL="WARN"
16 | [ -z "$LOG_CONFIG" ] && LOG_CONFIG="/app/monitors/log4j2.xml"
17 |
18 | #==============================================================================================
19 | # System properties
20 | # Logback configuration file and default level
21 | SYS_OPTS="-Dlog4j.configurationFile=file:$LOG_CONFIG -DLogLevel=$LOG_LEVEL"
22 |
23 | # Check on available log directories
24 | if [ -d "/app/log" ] ; then # Kubernetes style
25 | LOGDIR="/app/log"
26 | else
27 | LOGDIR="/app/dumps" # "default" for local deployments
28 | fi
29 | SYS_OPTS="$SYS_OPTS -Dlog.dir=$LOGDIR"
30 | #echo "SYS_OPTS = $SYS_OPTS"
31 |
32 | #==============================================================================================
33 | # Run
34 | echo "java ${SYS_OPTS} -cp ${CP} com.secretsagent.Main $@"
35 | java ${SYS_OPTS} -cp ${CP} com.secretsagent.Main $@
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##########################################################################
4 | # Run a "Local" docker image on the workstation
5 | #
6 | # 1) builds the image
7 | # 2) starts the server and shuts down as soon as job completes
8 | # 3) cleanly shuts down on ^C
9 |
10 | SERVICE_NAME="AWS Secrets Agent"
11 | SERVICE_LOG="AwsSecretsAgent.log"
12 |
13 | # Container and Image name
14 | NAME="awssec"
15 | IMAGE="${NAME}:latest"
16 |
17 | ##########################################################################
18 | # Changes terminal window name/tab
19 | set_title() {
20 | # tab
21 | echo -ne "\033]30;$SERVICE_NAME ($1)\007"
22 | # window
23 | echo -ne "\033]0;$SERVICE_NAME ($1)\007"
24 | }
25 |
26 | ##########################################################################
27 | # Kills everything we started
28 | kill_it() {
29 | # attempt to gracefully exit
30 | docker exec ${NAME} /app/bin/stop.sh
31 | # echo -e "\nSleeping for 10 seconds for graceful shutdown...\n"
32 | # sleep 10
33 | docker rm -f ${NAME}
34 | set_title "Stopped"
35 | }
36 |
37 | ##########################################################################
38 | # Script starts here
39 | ##########################################################################
40 | DIR=$(dirname $0)
41 | [ "$DIR" = "." ] && DIR=$(pwd)
42 |
43 | # this is used to get aws credentials when running in a local docker container
44 | AWS_DIR=$(cd $HOME/.aws ; pwd)
45 |
46 | # stop any running containers with this name
47 | docker rm -f ${NAME}
48 |
49 | # build the image locally
50 | #mvn -U clean install || exit 1
51 |
52 | # set up the logs directory in a way that it can be easily deleted
53 | mkdir -p ${DIR}/target/logs
54 |
55 | # trap INT so we can kill the background server task
56 | trap 'kill_it' 2
57 |
58 | echo
59 | echo "========================================================================="
60 | echo "Starting Docker image $NAME"
61 | echo "========================================================================="
62 | set_title "Running"
63 |
64 | # this runs and exits as soon as the job is complete
65 | docker run --rm --name=${NAME} \
66 | --user=$(id -u):$(id -g) \
67 | -v ${DIR}/local/config:/app/config \
68 | -v ${DIR}/local/secrets:/app/secrets \
69 | -v ${DIR}/target/logs:/app/dumps \
70 | -v ${AWS_DIR}:/.aws \
71 | ${IMAGE}
72 |
--------------------------------------------------------------------------------
/src/test/java/com/secretsagent/SecretsManagerTest.java:
--------------------------------------------------------------------------------
1 | package com.secretsagent;
2 |
3 | import com.amazonaws.services.secretsmanager.AWSSecretsManager;
4 | import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
5 | import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
6 | import org.mockito.Mock;
7 | import org.mockito.Mockito;
8 | import org.mockito.MockitoAnnotations;
9 | import org.testng.Assert;
10 | import org.testng.annotations.AfterMethod;
11 | import org.testng.annotations.BeforeMethod;
12 | import org.testng.annotations.Test;
13 |
14 | import static org.mockito.ArgumentMatchers.any;
15 | import static org.mockito.Mockito.when;
16 |
17 | /**
18 | * Copyright 2020 Comcast Cable Communications Management, LLC
19 | *
20 | * Licensed under the Apache License, Version2.0 (the "License");
21 | * you may not use this file except in compliance with the License.
22 | * You may obtain a copy of the License at
23 | *
24 | * http://www.apache.org/licenses/LICENSE-2.0
25 | *
26 | * Unless required by applicable law or agreed to in writing, software
27 | * distributed under the License is distributed on an "AS IS" BASIS,
28 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29 | * See the License for the specific language governing permissions and
30 | * limitations under the License.
31 | *
32 | * SPDX-License-Identifier: Apache-2.0
33 | */
34 |
35 | public class SecretsManagerTest
36 | {
37 | @Mock
38 | AWSSecretsManager awsSecretsManagerClient;
39 |
40 | @BeforeMethod
41 | public void setUp()
42 | {
43 | MockitoAnnotations.initMocks(this);
44 | }
45 |
46 | @AfterMethod
47 | public void tearDown()
48 | {
49 | // report Mockito problems in the test where they occur (default is in the next run)
50 | Mockito.validateMockitoUsage();
51 |
52 | // this may be needed to reset the mock
53 | Mockito.reset(awsSecretsManagerClient);
54 | }
55 |
56 | @Test
57 | public void testGetSecret_success()
58 | {
59 | String testSecretName = "my-secret";
60 | String expectedSecret = "my-secret-value";
61 |
62 | GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
63 | .withSecretId(testSecretName);
64 |
65 | GetSecretValueResult getSecretValueResult = new GetSecretValueResult()
66 | .withSecretString(expectedSecret);
67 |
68 | when(awsSecretsManagerClient.getSecretValue(getSecretValueRequest)).thenReturn(getSecretValueResult);
69 |
70 | // test
71 | SecretsManager secretsManager = new SecretsManager(awsSecretsManagerClient);
72 | String actualSecret = secretsManager.getSecret(testSecretName);
73 |
74 | // verify
75 | Assert.assertEquals(actualSecret, expectedSecret);
76 |
77 | // verify getSecretValue is called only once and with the expected values
78 | Mockito.verify(awsSecretsManagerClient).getSecretValue(any(GetSecretValueRequest.class));
79 | Mockito.verify(awsSecretsManagerClient).getSecretValue(getSecretValueRequest);
80 | }
81 | }
--------------------------------------------------------------------------------
/src/test/java/com/secretsagent/SecretsProcessorTest.java:
--------------------------------------------------------------------------------
1 | package com.secretsagent;
2 |
3 | import org.mockito.Mock;
4 | import org.mockito.Mockito;
5 | import org.mockito.MockitoAnnotations;
6 | import org.testng.Assert;
7 | import org.testng.annotations.AfterMethod;
8 | import org.testng.annotations.BeforeMethod;
9 | import org.testng.annotations.Test;
10 |
11 | import java.nio.file.Paths;
12 | import java.util.Arrays;
13 | import java.util.List;
14 | import java.util.Properties;
15 |
16 | import static org.mockito.ArgumentMatchers.anyString;
17 | import static org.mockito.Mockito.when;
18 |
19 | /**
20 | * Copyright 2020 Comcast Cable Communications Management, LLC
21 | *
22 | * Licensed under the Apache License, Version2.0 (the "License");
23 | * you may not use this file except in compliance with the License.
24 | * You may obtain a copy of the License at
25 | *
26 | * http://www.apache.org/licenses/LICENSE-2.0
27 | *
28 | * Unless required by applicable law or agreed to in writing, software
29 | * distributed under the License is distributed on an "AS IS" BASIS,
30 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31 | * See the License for the specific language governing permissions and
32 | * limitations under the License.
33 | *
34 | * SPDX-License-Identifier: Apache-2.0
35 | */
36 |
37 | public class SecretsProcessorTest
38 | {
39 | @Mock
40 | SecretsManager secretsManager;
41 |
42 | Properties properties;
43 |
44 | @BeforeMethod
45 | public void setUp()
46 | {
47 | MockitoAnnotations.initMocks(this);
48 |
49 | properties = new Properties();
50 | properties.setProperty("secret.name", "test-secret");
51 |
52 | String directoryPath = Paths.get(System.getProperty("user.dir"), "/local").toString();
53 | properties.setProperty("directory", directoryPath);
54 | properties.setProperty("secrets.filename", "/secrets/secrets.properties");
55 | }
56 |
57 | @AfterMethod
58 | public void tearDown()
59 | {
60 | // report Mockito problems in the test where they occur (default is in the next run)
61 | Mockito.validateMockitoUsage();
62 |
63 | // this may be needed to reset the mock
64 | Mockito.reset(secretsManager);
65 | }
66 |
67 | @Test
68 | public void testProcessSecret()
69 | {
70 | String secretString = "service.status.encryptedPassword=XXXXXXXXXXX==\nauth.accountcache.superUserPasswordEncrypted=XXXXXXXXXXX==\nauth" +
71 | ".accountcache.superUserName=service/user.test@testdomain.com\ndomainregistry.testdomain.auth" +
72 | ".passwordEncrypted=XXXXX==\ndomainregistry.testdomain.auth.userName=service/user.test@testdomain.com";
73 |
74 | List expectedResult = Arrays.asList(secretString.split("\\\\n"));
75 |
76 | when(secretsManager.getSecret(properties.getProperty("secret.name"))).thenReturn(secretString);
77 |
78 | // test
79 | SecretsProcessor secretsProcessor = new SecretsProcessor(secretsManager, properties);
80 | List actualResult = secretsProcessor.processSecret();
81 |
82 | // verify
83 | Assert.assertEquals(actualResult, expectedResult);
84 |
85 | // verify getSecretValue is called only once and with the expected values
86 | Mockito.verify(secretsManager).getSecret(anyString());
87 | Mockito.verify(secretsManager).getSecret(properties.getProperty("secret.name"));
88 | }
89 | }
--------------------------------------------------------------------------------
/src/main/java/com/secretsagent/SecretsManager.java:
--------------------------------------------------------------------------------
1 | package com.secretsagent;
2 |
3 | import com.amazonaws.auth.AWSCredentialsProvider;
4 | import com.amazonaws.auth.InstanceProfileCredentialsProvider;
5 | import com.amazonaws.auth.profile.ProfileCredentialsProvider;
6 | import com.amazonaws.auth.profile.ProfilesConfigFile;
7 | import com.amazonaws.services.secretsmanager.AWSSecretsManager;
8 | import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
9 | import com.amazonaws.services.secretsmanager.model.*;
10 | import org.apache.logging.log4j.Logger;
11 | import org.apache.logging.log4j.LogManager;
12 |
13 | /**
14 | * Copyright 2020 Comcast Cable Communications Management, LLC
15 | *
16 | * Licensed under the Apache License, Version2.0 (the "License");
17 | * you may not use this file except in compliance with the License.
18 | * You may obtain a copy of the License at
19 | *
20 | * http://www.apache.org/licenses/LICENSE-2.0
21 | *
22 | * Unless required by applicable law or agreed to in writing, software
23 | * distributed under the License is distributed on an "AS IS" BASIS,
24 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25 | * See the License for the specific language governing permissions and
26 | * limitations under the License.
27 | *
28 | * SPDX-License-Identifier: Apache-2.0
29 | */
30 |
31 |
32 | public class SecretsManager
33 | {
34 | private static final Logger logger = LogManager.getLogger(SecretsManager.class);
35 |
36 | private AWSSecretsManager awsSecretsManagerClient;
37 |
38 | public SecretsManager(AWSSecretsManager awsSecretsManagerClient)
39 | {
40 | this.awsSecretsManagerClient = awsSecretsManagerClient;
41 | }
42 |
43 | public SecretsManager(boolean isLocalRun, String region)
44 | {
45 | AWSCredentialsProvider credentials = getAwsCredentialsProvider(isLocalRun);
46 |
47 | this.awsSecretsManagerClient = AWSSecretsManagerClientBuilder.standard()
48 | .withCredentials(credentials)
49 | .withRegion(region)
50 | .build();
51 | }
52 |
53 | public String getSecret(String secretName)
54 | {
55 | GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
56 | .withSecretId(secretName);
57 | GetSecretValueResult getSecretValueResult = null;
58 |
59 | try
60 | {
61 | logger.info("Calling AWS SecretsManager");
62 | getSecretValueResult = awsSecretsManagerClient.getSecretValue(getSecretValueRequest);
63 | }
64 | catch (DecryptionFailureException e)
65 | {
66 | logger.error("Secrets Manager can't decrypt the protected secret text using the provided KMS key.");
67 | throw e;
68 | }
69 | catch (InternalServiceErrorException e)
70 | {
71 | logger.error("An error occurred on the server side.");
72 | throw e;
73 | }
74 | catch (InvalidParameterException e)
75 | {
76 | logger.error("An invalid value was provided for a parameter.");
77 | throw e;
78 | }
79 | catch (InvalidRequestException e)
80 | {
81 | logger.error("A parameter value was provided that is not valid for the current state of the resource.");
82 | throw e;
83 | }
84 | catch (ResourceNotFoundException e)
85 | {
86 | logger.error("Can't find the resource that was requested.");
87 | throw e;
88 | }
89 |
90 | // Returns decrypted secret using the associated KMS CMK.
91 | return getSecretValueResult.getSecretString();
92 | }
93 |
94 | private AWSCredentialsProvider getAwsCredentialsProvider(Boolean useLocalCredentials)
95 | {
96 | if (useLocalCredentials)
97 | {
98 | return new ProfileCredentialsProvider(new ProfilesConfigFile("../.aws/credentials"), "default");
99 | }
100 | else
101 | {
102 | return new InstanceProfileCredentialsProvider(false);
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/secretsagent/SecretsProcessor.java:
--------------------------------------------------------------------------------
1 | package com.secretsagent;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.apache.logging.log4j.Logger;
5 | import org.apache.logging.log4j.LogManager;
6 |
7 | import java.io.File;
8 | import java.io.FileReader;
9 | import java.io.IOException;
10 | import java.nio.charset.StandardCharsets;
11 | import java.nio.file.Files;
12 | import java.nio.file.Path;
13 | import java.nio.file.Paths;
14 | import java.util.Arrays;
15 | import java.util.List;
16 | import java.util.Properties;
17 |
18 | /**
19 | * Copyright 2020 Comcast Cable Communications Management, LLC
20 | *
21 | * Licensed under the Apache License, Version2.0 (the "License");
22 | * you may not use this file except in compliance with the License.
23 | * You may obtain a copy of the License at
24 | *
25 | * http://www.apache.org/licenses/LICENSE-2.0
26 | *
27 | * Unless required by applicable law or agreed to in writing, software
28 | * distributed under the License is distributed on an "AS IS" BASIS,
29 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30 | * See the License for the specific language governing permissions and
31 | * limitations under the License.
32 | *
33 | * SPDX-License-Identifier: Apache-2.0
34 | */
35 |
36 | public class SecretsProcessor
37 | {
38 | public static final String SERVICE_PROPERTIES = "/config/service.properties";
39 | public static final String CONTAINER_DIRECTORY = "/app";
40 | public static final String LOCAL_DIRECTORY = "/local";
41 |
42 | private static final Logger logger = LogManager.getLogger(SecretsProcessor.class);
43 |
44 | SecretsManager secretsManager;
45 | Properties properties;
46 |
47 | public SecretsProcessor(SecretsManager secretsManager, Properties properties)
48 | {
49 | this.secretsManager = secretsManager;
50 | this.properties = properties;
51 | }
52 |
53 | public SecretsProcessor()
54 | {
55 | this.properties = getProperties();
56 |
57 | String region = properties.getProperty("region");
58 | boolean isLocalRun = false;
59 | if (StringUtils.isNotEmpty(properties.getProperty("is.local.run")))
60 | {
61 | isLocalRun = Boolean.parseBoolean(properties.getProperty("is.local.run"));
62 | }
63 |
64 | this.secretsManager = new SecretsManager(isLocalRun, region);
65 | }
66 |
67 | public List processSecret()
68 | {
69 | // *** get secret
70 | String secretName = properties.getProperty("secret.name");
71 | String secret = secretsManager.getSecret(secretName);
72 |
73 | // *** write file
74 | List lines = Arrays.asList(secret.split("\\\\n"));
75 |
76 | Path secretsPath = Paths.get(properties.getProperty("directory"), properties.getProperty("secrets.filename"));
77 |
78 | try
79 | {
80 | writeFile(lines, secretsPath);
81 | }
82 | catch (IOException ex)
83 | {
84 | logger.error("Error writing to file", ex);
85 | }
86 |
87 | logger.info("Completed successfully.");
88 |
89 | // return lines for testing
90 | return lines;
91 | }
92 |
93 | private Properties getProperties()
94 | {
95 | Properties properties = new Properties();
96 |
97 | Path directoryPath = Paths.get(CONTAINER_DIRECTORY);
98 |
99 | if (Files.notExists(directoryPath))
100 | {
101 | directoryPath = Paths.get(System.getProperty("user.dir"), LOCAL_DIRECTORY);
102 | }
103 | String directory = directoryPath.toString();
104 |
105 | Path propertiesPath = Paths.get(directoryPath.toString(), SERVICE_PROPERTIES);
106 |
107 | try
108 | {
109 | properties.load(new FileReader(String.valueOf(propertiesPath)));
110 | }
111 | catch (IOException e)
112 | {
113 | logger.warn("Unable to load properties.");
114 | }
115 |
116 | properties.setProperty("directory", directory);
117 |
118 | return properties;
119 | }
120 |
121 | private void writeFile(List lines, Path path) throws IOException
122 | {
123 | logger.info("Attempting to write to: {}", path);
124 |
125 | // create the parent directories if they don't exist
126 | File file = new File(path.toString());
127 | file.getParentFile().mkdirs();
128 |
129 | Files.write(path, lines, StandardCharsets.UTF_8);
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AWS Secrets Agent
2 |
3 | Reads secrets from AWS SecretsManager and writes to a file. Intended to be run as a kubernetes Init Container to create a secrets.properties file in a volume mount that is
4 | accessible to other containers in the pod.
5 |
6 | ## Configuration Settings
7 | | Key | Required | Description | Example value |
8 | | :--- | :--- | :--- | :--- |
9 | | `secret.name` | Yes | Friendly name of the AWS Secret | `valws/lab.main.t.aor.valws.vaw2` |
10 | | `region` | Yes | AWS Region where secrets are stored | `us-east-1` |
11 | | `secrets.filename` | Yes | The relative path and filename of the output file. | `/secrets/secrets.properties` |
12 | | `is.local.run` | No | Whether this is a local run. Defaults to `false` | `true` |
13 |
14 | If `is.local.run` is `true`:
15 | * /local/config/service.properties is used.
16 | * local AWS credentials are used.
17 |
18 | ## Environment Variables
19 | | Variable | Required | Description | Example value |
20 | | :--- | :--- | :--- | :--- |
21 | | `DOCKER_REPO` | Yes | The Docker repository to deploy the image to | `testrepo` |
22 |
23 | Alternatively, this can be passed in to the maven command using the `-D` option. For example:
24 | `mvn clean install -Denv.DOCKER_REPO=test`
25 |
26 | ## To run and test locally
27 | Run `run.sh`
28 | This runs the Docker container.
29 |
30 | ## Example Kubernetes configurations
31 |
32 | ### Deployment.yaml
33 | * An IAM Role with permissions to access SecretsManager is required.
34 | * AWS Secrets Agent is configured as an Init Container.
35 | * AWS Secrets Agent mounts and writes to a volume that is also mounted by another container in the pod.
36 | * AWS Secrets Agent mounts the config volume and location that matches ConfigMap.yaml.
37 |
38 | In the example below, the following keys are used for AWS Secrets Agent:
39 | * `spec.template.metadata.annotations.iam.amazonaws.com/role`
40 | * `spec.template.spec.initContainers`
41 | * `spec.template.spec.containers..volumeMounts.`
42 | * `spec.template.spec.volumes.`
43 | * `spec.template.spec.volumes.`
44 |
45 | ```yaml
46 | ---
47 | apiVersion: apps/v1
48 | kind: Deployment
49 | metadata:
50 | name: lab-main-t-aor-valws-vaw2
51 | namespace: workflow
52 | labels:
53 | environmentShortName: lab
54 | regionShortName: main
55 | partitionShortName: t
56 | serviceShortName: valws
57 | clusterSuffix: vaw2
58 | clusterTitle: lab-main-t-aor-valws-vaw2
59 | spec:
60 | replicas: 2
61 | revisionHistoryLimit: 2
62 | strategy:
63 | type: RollingUpdate
64 | rollingUpdate:
65 | maxSurge: 25%
66 | maxUnavailable: 25%
67 | selector:
68 | matchLabels:
69 | clusterTitle: lab-main-t-aor-valws-vaw2
70 | template:
71 | metadata:
72 | annotations:
73 | iam.amazonaws.com/role: arn:aws:iam::0000000001:role/CustomerManaged-ValidationWebService
74 | labels:
75 | environmentShortName: lab
76 | regionShortName: main
77 | partitionShortName: t
78 | serviceShortName: valws
79 | clusterSuffix: vaw2
80 | clusterTitle: lab-main-t-aor-valws-vaw2
81 | spec:
82 | initContainers:
83 | - name: awssec
84 | image: docker-lab.repo.com/awssec:0.0.3
85 | volumeMounts:
86 | - name: awssec-config
87 | mountPath: /app/config
88 | - name: app-secrets
89 | mountPath: /app/secrets
90 | containers:
91 | - name: valws
92 | image: docker-lab.repo.com/valws:2.0.8
93 | imagePullPolicy: Always
94 | ports:
95 | - containerPort: 8080
96 | name: service
97 | resources:
98 | requests:
99 | cpu: 100m
100 | memory: 2048Mi
101 | limits:
102 | cpu: 4000m
103 | memory: 2048Mi
104 | readinessProbe:
105 | httpGet:
106 | path: validation/management/alive
107 | port: 8080
108 | initialDelaySeconds: 10
109 | periodSeconds: 10
110 | timeoutSeconds: 60
111 | successThreshold: 1
112 | failureThreshold: 30
113 | livenessProbe:
114 | httpGet:
115 | path: validation/management/alive
116 | port: 8080
117 | initialDelaySeconds: 30
118 | periodSeconds: 20
119 | timeoutSeconds: 60
120 | successThreshold: 1
121 | failureThreshold: 3
122 | volumeMounts:
123 | - name: config
124 | mountPath: /app/config
125 | - name: app-secrets
126 | mountPath: /app/secrets
127 | volumes:
128 | - name: config
129 | configMap:
130 | name: lab-main-t-aor-valws-vaw2
131 | items:
132 | - key: service-properties
133 | path: service.properties
134 | - key: env-properties
135 | path: env.properties
136 | - name: awssec-config
137 | configMap:
138 | name: lab-main-t-aor-valws-vaw2
139 | items:
140 | - key: awssec-properties
141 | path: service.properties
142 | - key: awssec-env-properties
143 | path: env.properties
144 | - name: app-secrets
145 | emptyDir: {}
146 |
147 | ```
148 |
149 | ### ConfigMap.yaml
150 | In the example below, the following keys are used for AWS Secrets Agent:
151 | * `data.awssec-env-properties`
152 | * `data.awssec-properties`
153 |
154 | ```yaml
155 | ---
156 | apiVersion: v1
157 | kind: ConfigMap
158 | metadata:
159 | name: lab-main-t-aor-valws-vaw2
160 | namespace: workflow
161 | labels:
162 | environmentShortName: lab
163 | regionShortName: main
164 | partitionShortName: t
165 | serviceShortName: valws
166 | clusterSuffix: vaw2
167 | clusterTitle: lab-main-t-aor-valws-vaw2
168 | data:
169 | env-properties: |
170 | ALIVE_MBEAN="application=ValidationService,name=aliveCheckConfiguration"
171 | service-properties: |
172 | ws.base.url=http://validation.test.corp.mycompany.com/validation/web
173 | ws.instance.name=Test
174 | awssec-env-properties: |
175 | LOG_CONFIG=/app/monitors/logback-syslog.xml
176 | LOG_LEVEL=INFO
177 | awssec-properties: |
178 | secret.name=valws/lab.main.t.aor.valws.vaw2
179 | region=us-east-1
180 | secrets.filename=/secrets/secrets.properties
181 |
182 | ```
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 |
8 | com.secretsagent
9 | aws-secrets-agent
10 | 1.0.0
11 | jar
12 |
13 | AWS Secrets Agent
14 |
15 |
16 | awssec
17 | 1.8
18 | java18
19 | 1.11.816
20 | 2.11.1
21 | 1.14
22 | 3.10
23 | 2.13.3
24 | 3.3.3
25 | 7.1.0
26 |
27 |
28 |
29 |
30 | com.amazonaws
31 | aws-java-sdk-secretsmanager
32 | ${aws.java.sdk.version}
33 |
34 |
35 | com.fasterxml.jackson.core
36 | jackson-core
37 |
38 |
39 | com.fasterxml.jackson.core
40 | jackson-databind
41 |
42 |
43 | commons-codec
44 | commons-codec
45 |
46 |
47 |
48 |
49 | com.fasterxml.jackson.core
50 | jackson-core
51 | ${jackson.version}
52 |
53 |
54 | com.fasterxml.jackson.core
55 | jackson-databind
56 | ${jackson.version}
57 |
58 |
59 | commons-codec
60 | commons-codec
61 | ${commons.codec.version}
62 |
63 |
64 | org.apache.commons
65 | commons-lang3
66 | ${commons.lang3.version}
67 |
68 |
69 | org.apache.logging.log4j
70 | log4j-core
71 | ${log4j.version}
72 |
73 |
74 |
75 |
76 | org.testng
77 | testng
78 | ${testng.version}
79 | test
80 |
81 |
82 | org.mockito
83 | mockito-core
84 | ${mockito.version}
85 | test
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | maven-compiler-plugin
94 | 3.8.1
95 |
96 | 1.8
97 | 1.8
98 |
99 |
100 |
101 |
102 | maven-jar-plugin
103 | 3.2.0
104 |
105 |
106 |
107 | true
108 | lib/
109 | com.secretsagent.Main
110 |
111 |
112 |
113 |
114 |
115 | org.apache.maven.plugins
116 | maven-dependency-plugin
117 |
118 |
119 |
120 | copy-dependencies
121 |
122 | package
123 |
124 |
125 |
126 | runtime
127 |
128 |
129 |
130 | com.spotify
131 | dockerfile-maven-plugin
132 | 1.4.6
133 |
134 |
135 |
136 | default
137 |
138 | build
139 | push
140 |
141 |
142 |
143 | tag-local-version
144 |
145 | tag
146 |
147 |
148 | ${docker.imageName}
149 | ${project.version}
150 |
151 |
152 |
153 | tag-local-latest
154 |
155 | tag
156 |
157 |
158 | ${docker.imageName}
159 | latest
160 |
161 |
162 |
163 |
164 | ${env.DOCKER_REPO}/${docker.imageName}
165 | ${project.version}
166 | false
167 |
168 | ${project.artifactId}-${project.version}.jar
169 |
170 |
171 |
172 |
173 |
174 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------