Skip to content

Commit 1140eff

Browse files
committed
Merge branch 'hkobew/ec2/useActions' into feature/ec2
2 parents 754d0fc + ca86046 commit 1140eff

File tree

4 files changed

+32
-60
lines changed

4 files changed

+32
-60
lines changed

src/ec2/model.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ import { isCloud9 } from '../shared/extensionUtilities'
1212
import { ToolkitError } from '../shared/errors'
1313
import { SsmClient } from '../shared/clients/ssmClient'
1414
import { Ec2Client } from '../shared/clients/ec2Client'
15-
import { VscodeRemoteConnection, ensureDependencies, openRemoteTerminal } from '../shared/remoteSession'
15+
import {
16+
VscodeRemoteConnection,
17+
ensureDependencies,
18+
getDeniedSsmActions,
19+
openRemoteTerminal,
20+
} from '../shared/remoteSession'
1621
import { DefaultIamClient } from '../shared/clients/iamClient'
1722
import { ErrorInformation } from '../shared/errors'
1823
import { sshAgentSocketVariable, startSshAgent, startVscodeRemote } from '../shared/extensions/ssh'
@@ -72,13 +77,10 @@ export class Ec2ConnectionManager {
7277
}
7378
}
7479

75-
public async hasProperPolicies(IamRoleArn: string): Promise<boolean> {
76-
const attachedPolicies = (await this.iamClient.listAttachedRolePolicies(IamRoleArn)).map(
77-
policy => policy.PolicyName!
78-
)
79-
const requiredPolicies = ['AmazonSSMManagedInstanceCore', 'AmazonSSMManagedEC2InstanceDefaultPolicy']
80+
public async hasProperPermissions(IamRoleArn: string): Promise<boolean> {
81+
const deniedActions = await getDeniedSsmActions(this.iamClient, IamRoleArn)
8082

81-
return requiredPolicies.length !== 0 && requiredPolicies.every(policy => attachedPolicies.includes(policy))
83+
return deniedActions.length === 0
8284
}
8385

8486
public async isInstanceRunning(instanceId: string): Promise<boolean> {
@@ -108,10 +110,10 @@ export class Ec2ConnectionManager {
108110
this.throwConnectionError(message, selection, { code: 'EC2SSMPermission' })
109111
}
110112

111-
const hasProperPolicies = await this.hasProperPolicies(IamRole!.Arn)
113+
const hasPermission = await this.hasProperPermissions(IamRole!.Arn)
112114

113-
if (!hasProperPolicies) {
114-
const message = `Ensure an IAM role with the required policies is attached to the instance. Found attached role: ${
115+
if (!hasPermission) {
116+
const message = `Ensure an IAM role with the proper permissions is attached to the instance. Found attached role: ${
115117
IamRole!.Arn
116118
}`
117119
this.throwConnectionError(message, selection, {

src/ecs/util.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { TaskDefinition } from 'aws-sdk/clients/ecs'
1818
import { getLogger } from '../shared/logger'
1919
import { SSM } from 'aws-sdk'
2020
import { fromExtensionManifest } from '../shared/settings'
21+
import { getDeniedSsmActions } from '../shared/remoteSession'
2122

2223
interface EcsTaskIdentifer {
2324
readonly task: string
@@ -41,15 +42,7 @@ export async function checkPermissionsForSsm(
4142
})
4243
}
4344

44-
const deniedActions = await client.getDeniedActions({
45-
PolicySourceArn: task.taskRoleArn,
46-
ActionNames: [
47-
'ssmmessages:CreateControlChannel',
48-
'ssmmessages:CreateDataChannel',
49-
'ssmmessages:OpenControlChannel',
50-
'ssmmessages:OpenDataChannel',
51-
],
52-
})
45+
const deniedActions = await getDeniedSsmActions(client, task.taskRoleArn)
5346

5447
if (deniedActions.length !== 0) {
5548
const message = localize(

src/shared/remoteSession.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import { SystemUtilities } from './systemUtilities'
1919
import { getOrInstallCli } from './utilities/cliUtils'
2020
import { pushIf } from './utilities/collectionUtils'
2121
import { ChildProcess } from './utilities/childProcess'
22+
import { IamClient } from './clients/iamClient'
23+
import { IAM } from 'aws-sdk'
2224

2325
export interface MissingTool {
2426
readonly name: 'code' | 'ssm' | 'ssh'
@@ -167,3 +169,17 @@ export async function handleMissingTool(tools: Err<MissingTool[]>) {
167169
})
168170
)
169171
}
172+
173+
export async function getDeniedSsmActions(client: IamClient, roleArn: string): Promise<IAM.EvaluationResult[]> {
174+
const deniedActions = await client.getDeniedActions({
175+
PolicySourceArn: roleArn,
176+
ActionNames: [
177+
'ssmmessages:CreateControlChannel',
178+
'ssmmessages:CreateDataChannel',
179+
'ssmmessages:OpenControlChannel',
180+
'ssmmessages:OpenDataChannel',
181+
],
182+
})
183+
184+
return deniedActions
185+
}

src/test/ec2/model.test.ts

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -42,45 +42,6 @@ describe('Ec2ConnectClient', function () {
4242
})
4343
})
4444

45-
describe('hasProperPolicies', async function () {
46-
it('correctly determines if proper policies are included', async function () {
47-
async function assertAcceptsPolicies(policies: IAM.Policy[], expectedResult: boolean) {
48-
sinon.stub(DefaultIamClient.prototype, 'listAttachedRolePolicies').resolves(policies)
49-
50-
const result = await client.hasProperPolicies('')
51-
assert.strictEqual(result, expectedResult)
52-
53-
sinon.restore()
54-
}
55-
await assertAcceptsPolicies(
56-
[{ PolicyName: 'name' }, { PolicyName: 'name2' }, { PolicyName: 'name3' }],
57-
false
58-
)
59-
await assertAcceptsPolicies(
60-
[
61-
{ PolicyName: 'AmazonSSMManagedInstanceCore' },
62-
{ PolicyName: 'AmazonSSMManagedEC2InstanceDefaultPolicy' },
63-
],
64-
true
65-
)
66-
await assertAcceptsPolicies([{ PolicyName: 'AmazonSSMManagedEC2InstanceDefaultPolicy' }], false)
67-
await assertAcceptsPolicies([{ PolicyName: 'AmazonSSMManagedEC2InstanceDefaultPolicy' }], false)
68-
})
69-
70-
it('throws error when sdk throws error', async function () {
71-
sinon.stub(DefaultIamClient.prototype, 'listAttachedRolePolicies').throws(new ToolkitError('error'))
72-
73-
try {
74-
await client.hasProperPolicies('')
75-
assert.ok(false)
76-
} catch {
77-
assert.ok(true)
78-
}
79-
80-
sinon.restore()
81-
})
82-
})
83-
8445
describe('isInstanceRunning', async function () {
8546
it('only returns true with the instance is running', async function () {
8647
sinon.stub(Ec2Client.prototype, 'getInstanceStatus').callsFake(async (input: string) => input.split(':')[0])
@@ -131,7 +92,7 @@ describe('Ec2ConnectClient', function () {
13192
it('throws EC2SSMAgent error if instance is running and has IAM Role, but agent is not running', async function () {
13293
sinon.stub(Ec2ConnectionManager.prototype, 'isInstanceRunning').resolves(true)
13394
sinon.stub(Ec2ConnectionManager.prototype, 'getAttachedIamRole').resolves({ Arn: 'testRole' } as IAM.Role)
134-
sinon.stub(Ec2ConnectionManager.prototype, 'hasProperPolicies').resolves(true)
95+
sinon.stub(Ec2ConnectionManager.prototype, 'hasProperPermissions').resolves(true)
13596
sinon.stub(SsmClient.prototype, 'getInstanceAgentPingStatus').resolves('offline')
13697

13798
try {
@@ -147,7 +108,7 @@ describe('Ec2ConnectClient', function () {
147108
it('does not throw an error if all checks pass', async function () {
148109
sinon.stub(Ec2ConnectionManager.prototype, 'isInstanceRunning').resolves(true)
149110
sinon.stub(Ec2ConnectionManager.prototype, 'getAttachedIamRole').resolves({ Arn: 'testRole' } as IAM.Role)
150-
sinon.stub(Ec2ConnectionManager.prototype, 'hasProperPolicies').resolves(true)
111+
sinon.stub(Ec2ConnectionManager.prototype, 'hasProperPermissions').resolves(true)
151112
sinon.stub(SsmClient.prototype, 'getInstanceAgentPingStatus').resolves('Online')
152113

153114
assert.doesNotThrow(async () => await client.checkForStartSessionError(instanceSelection))

0 commit comments

Comments
 (0)