Initial commit: AWS CDK stack and Priority Listing
This commit is contained in:
commit
5cef51b0f6
13 changed files with 6320 additions and 0 deletions
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
# Node dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# CDK build outputs
|
||||||
|
aws_cdk/cdk.out/
|
||||||
|
aws_cdk/.jsii
|
||||||
|
aws_cdk/tsconfig.tsbuildinfo
|
||||||
|
aws_cdk/**/*.js
|
||||||
|
aws_cdk/**/*.d.ts
|
||||||
|
|
||||||
|
# Environments & IDEs
|
||||||
|
.env
|
||||||
|
.DS_Store
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
8
aws_cdk/.gitignore
vendored
Normal file
8
aws_cdk/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
*.js
|
||||||
|
!jest.config.js
|
||||||
|
*.d.ts
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# CDK asset staging directory
|
||||||
|
.cdk.staging
|
||||||
|
cdk.out
|
||||||
6
aws_cdk/.npmignore
Normal file
6
aws_cdk/.npmignore
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
*.ts
|
||||||
|
!*.d.ts
|
||||||
|
|
||||||
|
# CDK asset staging directory
|
||||||
|
.cdk.staging
|
||||||
|
cdk.out
|
||||||
14
aws_cdk/README.md
Normal file
14
aws_cdk/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Welcome to your CDK TypeScript project
|
||||||
|
|
||||||
|
This is a blank project for CDK development with TypeScript.
|
||||||
|
|
||||||
|
The `cdk.json` file tells the CDK Toolkit how to execute your app.
|
||||||
|
|
||||||
|
## Useful commands
|
||||||
|
|
||||||
|
* `npm run build` type-check the project
|
||||||
|
* `npm run watch` watch for changes and type-check
|
||||||
|
* `npm run test` perform the jest unit tests
|
||||||
|
* `npx cdk deploy` deploy this stack to your default AWS account/region
|
||||||
|
* `npx cdk diff` compare deployed stack with current state
|
||||||
|
* `npx cdk synth` emits the synthesized CloudFormation template
|
||||||
20
aws_cdk/bin/aws_cdk.ts
Normal file
20
aws_cdk/bin/aws_cdk.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import * as cdk from 'aws-cdk-lib/core';
|
||||||
|
import { AwsCdkStack } from '../lib/aws_cdk-stack';
|
||||||
|
|
||||||
|
const app = new cdk.App();
|
||||||
|
new AwsCdkStack(app, 'AwsCdkStack', {
|
||||||
|
/* If you don't specify 'env', this stack will be environment-agnostic.
|
||||||
|
* Account/Region-dependent features and context lookups will not work,
|
||||||
|
* but a single synthesized template can be deployed anywhere. */
|
||||||
|
|
||||||
|
/* Uncomment the next line to specialize this stack for the AWS Account
|
||||||
|
* and Region that are implied by the current CLI configuration. */
|
||||||
|
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
|
||||||
|
|
||||||
|
/* Uncomment the next line if you know exactly what Account and Region you
|
||||||
|
* want to deploy the stack to. */
|
||||||
|
// env: { account: '123456789012', region: 'us-east-1' },
|
||||||
|
|
||||||
|
/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
|
||||||
|
});
|
||||||
110
aws_cdk/cdk.json
Normal file
110
aws_cdk/cdk.json
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
{
|
||||||
|
"app": "npx tsc && npx tsx bin/aws_cdk.ts",
|
||||||
|
"watch": {
|
||||||
|
"include": [
|
||||||
|
"**"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"README.md",
|
||||||
|
"cdk*.json",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"**/*.js",
|
||||||
|
"tsconfig.json",
|
||||||
|
"package*.json",
|
||||||
|
"yarn.lock",
|
||||||
|
"node_modules",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
|
||||||
|
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
|
||||||
|
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
|
||||||
|
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
|
||||||
|
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
|
||||||
|
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
|
||||||
|
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
|
||||||
|
"@aws-cdk/aws-batch:defaultToAL2023": true,
|
||||||
|
"@aws-cdk/aws-cloudfront:defaultFunctionRuntimeV2_0": true,
|
||||||
|
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
|
||||||
|
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
|
||||||
|
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
|
||||||
|
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
|
||||||
|
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
|
||||||
|
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
|
||||||
|
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
|
||||||
|
"@aws-cdk/aws-dynamodb:retainTableReplica": true,
|
||||||
|
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
|
||||||
|
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
|
||||||
|
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
|
||||||
|
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
|
||||||
|
"@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true,
|
||||||
|
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
|
||||||
|
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
|
||||||
|
"@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
|
||||||
|
"@aws-cdk/aws-ecs-patterns:uniqueTargetGroupId": true,
|
||||||
|
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
|
||||||
|
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
|
||||||
|
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
|
||||||
|
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
|
||||||
|
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
|
||||||
|
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
|
||||||
|
"@aws-cdk/aws-eks:defaultToAL2023": true,
|
||||||
|
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
|
||||||
|
"@aws-cdk/aws-eks:useNativeOidcProvider": true,
|
||||||
|
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
|
||||||
|
"@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault": true,
|
||||||
|
"@aws-cdk/aws-elasticloadbalancingv2:usePostQuantumTlsPolicy": true,
|
||||||
|
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
|
||||||
|
"@aws-cdk/aws-events:requireEventBusPolicySid": true,
|
||||||
|
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
|
||||||
|
"@aws-cdk/aws-iam:minimizePolicies": true,
|
||||||
|
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
|
||||||
|
"@aws-cdk/aws-kms:aliasNameRef": true,
|
||||||
|
"@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true,
|
||||||
|
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
|
||||||
|
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
|
||||||
|
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
|
||||||
|
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false,
|
||||||
|
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
||||||
|
"@aws-cdk/aws-lambda:useCdkManagedLogGroup": true,
|
||||||
|
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
|
||||||
|
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
|
||||||
|
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
|
||||||
|
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
|
||||||
|
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
|
||||||
|
"@aws-cdk/aws-redshift:columnId": true,
|
||||||
|
"@aws-cdk/aws-route53-patterns:useDistribution": true,
|
||||||
|
"@aws-cdk/aws-route53-patters:useCertificate": true,
|
||||||
|
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
|
||||||
|
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
|
||||||
|
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
|
||||||
|
"@aws-cdk/aws-s3:publicAccessBlockedByDefault": true,
|
||||||
|
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
|
||||||
|
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true,
|
||||||
|
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
|
||||||
|
"@aws-cdk/aws-signer:signingProfileNamePassedToCfn": true,
|
||||||
|
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
|
||||||
|
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
|
||||||
|
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
|
||||||
|
"@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true,
|
||||||
|
"@aws-cdk/core:annotationsInValidationReport": true,
|
||||||
|
"@aws-cdk/core:aspectPrioritiesMutating": true,
|
||||||
|
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
|
||||||
|
"@aws-cdk/core:checkSecretUsage": true,
|
||||||
|
"@aws-cdk/core:defaultCrossStackReferences": "weak",
|
||||||
|
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
|
||||||
|
"@aws-cdk/core:enablePartitionLiterals": true,
|
||||||
|
"@aws-cdk/core:explicitStackTags": true,
|
||||||
|
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
|
||||||
|
"@aws-cdk/core:target-partitions": [
|
||||||
|
"aws",
|
||||||
|
"aws-cn"
|
||||||
|
],
|
||||||
|
"@aws-cdk/core:validateAgainstDefaultRules": true,
|
||||||
|
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
|
||||||
|
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
|
||||||
|
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
|
||||||
|
"@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true
|
||||||
|
}
|
||||||
|
}
|
||||||
9
aws_cdk/jest.config.js
Normal file
9
aws_cdk/jest.config.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
module.exports = {
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/test'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': ['@swc/jest']
|
||||||
|
},
|
||||||
|
setupFilesAfterEnv: ['aws-cdk-lib/testhelpers/jest-autoclean'],
|
||||||
|
};
|
||||||
87
aws_cdk/lib/aws_cdk-stack.ts
Normal file
87
aws_cdk/lib/aws_cdk-stack.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import * as cdk from 'aws-cdk-lib';
|
||||||
|
import { Construct } from 'constructs';
|
||||||
|
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
||||||
|
|
||||||
|
export class AwsCdkStack extends cdk.Stack {
|
||||||
|
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||||
|
super(scope, id, props);
|
||||||
|
|
||||||
|
// 1. Look up the existing Default VPC (Free, avoids NAT Gateway charges)
|
||||||
|
const vpc = ec2.Vpc.fromLookup(this, 'DefaultVpc', {
|
||||||
|
isDefault: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Create a Security Group for the Mail Server
|
||||||
|
const securityGroup = new ec2.SecurityGroup(this, 'MailServerSG', {
|
||||||
|
vpc,
|
||||||
|
description: 'Security Group for self-hosted mail server',
|
||||||
|
allowAllOutbound: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow SSH
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'Allow SSH access');
|
||||||
|
|
||||||
|
// Mail Protocols (SMTP, SMTPS, Submission)
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(25), 'Allow SMTP');
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(465), 'Allow SMTPS');
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(587), 'Allow SMTP Submission');
|
||||||
|
|
||||||
|
// Mail Protocols (IMAP, IMAPS)
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(143), 'Allow IMAP');
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(993), 'Allow IMAPS');
|
||||||
|
|
||||||
|
// Web Traffic (HTTP, HTTPS for Admin UI & Webmail / SSL certificates)
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP');
|
||||||
|
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'Allow HTTPS');
|
||||||
|
|
||||||
|
// 3. Define the EC2 Instance (t3.medium recommended for running ClamAV/Rspamd)
|
||||||
|
const instance = new ec2.Instance(this, 'MailServerInstance', {
|
||||||
|
vpc,
|
||||||
|
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
|
||||||
|
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MEDIUM),
|
||||||
|
machineImage: ec2.MachineImage.lookup({
|
||||||
|
name: 'ubuntu/images/hvm-ssd/ubuntu-noble-24.04-amd64-server-*',
|
||||||
|
owners: ['099720109477'], // Canonical owner ID
|
||||||
|
}),
|
||||||
|
securityGroup,
|
||||||
|
blockDevices: [
|
||||||
|
{
|
||||||
|
deviceName: '/dev/sda1',
|
||||||
|
volume: ec2.BlockDeviceVolume.ebs(60, { // 60GB Root Volume
|
||||||
|
volumeType: ec2.EbsDeviceVolumeType.GP3,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. Install Docker and Docker Compose via User Data
|
||||||
|
instance.addUserData(
|
||||||
|
'apt-get update -y',
|
||||||
|
'apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release',
|
||||||
|
'mkdir -m 0755 -p /etc/apt/keyrings',
|
||||||
|
'curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg',
|
||||||
|
'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null',
|
||||||
|
'apt-get update -y',
|
||||||
|
'apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin',
|
||||||
|
'systemctl enable docker',
|
||||||
|
'systemctl start docker'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5. Provision Elastic IP and Associate with EC2
|
||||||
|
const eip = new ec2.CfnEIP(this, 'MailServerEIP', {
|
||||||
|
domain: 'vpc',
|
||||||
|
});
|
||||||
|
|
||||||
|
new ec2.CfnEIPAssociation(this, 'MailServerEIPAssociation', {
|
||||||
|
eip: eip.ref,
|
||||||
|
instanceId: instance.instanceId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Outputs
|
||||||
|
new cdk.CfnOutput(this, 'MailServerPublicIP', {
|
||||||
|
value: eip.ref,
|
||||||
|
description: 'The Elastic IP address of your Mail Server',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
5892
aws_cdk/package-lock.json
generated
Normal file
5892
aws_cdk/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
24
aws_cdk/package.json
Normal file
24
aws_cdk/package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "aws_cdk",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"watch": "tsc -w",
|
||||||
|
"test": "jest",
|
||||||
|
"cdk": "cdk"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@swc/core": "^1.15.0",
|
||||||
|
"@swc/jest": "^0.2.39",
|
||||||
|
"@types/jest": "^30",
|
||||||
|
"@types/node": "^24.10.1",
|
||||||
|
"jest": "^30",
|
||||||
|
"aws-cdk": "2.1135.0",
|
||||||
|
"tsx": "^4.23.0",
|
||||||
|
"typescript": "~7.0.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"aws-cdk-lib": "^2.263.0",
|
||||||
|
"constructs": "^10.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
aws_cdk/test/aws_cdk.test.ts
Normal file
17
aws_cdk/test/aws_cdk.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// import * as cdk from 'aws-cdk-lib/core';
|
||||||
|
// import { Template } from 'aws-cdk-lib/assertions';
|
||||||
|
// import * as AwsCdk from '../lib/aws_cdk-stack';
|
||||||
|
|
||||||
|
// example test. To run these tests, uncomment this file along with the
|
||||||
|
// example resource in lib/aws_cdk-stack.ts
|
||||||
|
test('SQS Queue Created', () => {
|
||||||
|
// const app = new cdk.App();
|
||||||
|
// // WHEN
|
||||||
|
// const stack = new AwsCdk.AwsCdkStack(app, 'MyTestStack');
|
||||||
|
// // THEN
|
||||||
|
// const template = Template.fromStack(stack);
|
||||||
|
|
||||||
|
// template.hasResourceProperties('AWS::SQS::Queue', {
|
||||||
|
// VisibilityTimeout: 300
|
||||||
|
// });
|
||||||
|
});
|
||||||
38
aws_cdk/tsconfig.json
Normal file
38
aws_cdk/tsconfig.json
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"lib": [
|
||||||
|
"es2022"
|
||||||
|
],
|
||||||
|
"declaration": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"alwaysStrict": true,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"strictPropertyInitialization": false,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"typeRoots": [
|
||||||
|
"./node_modules/@types"
|
||||||
|
],
|
||||||
|
"noEmit": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"types": [
|
||||||
|
"jest",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"cdk.out"
|
||||||
|
]
|
||||||
|
}
|
||||||
80
priority_listing.md
Normal file
80
priority_listing.md
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
# Priority Listing: Self-Hosted Email Service on AWS EC2
|
||||||
|
|
||||||
|
This document outlines the step-by-step priority list to deploy and verify a self-hosted, Docker-based email server on AWS EC2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: AWS Configuration & Requests (Immediate Priority)
|
||||||
|
|
||||||
|
Before writing any configuration files or launching servers, you must set up the AWS infrastructure and submit request forms to AWS Support. **AWS manually reviews these requests, which can take 24–48 hours.**
|
||||||
|
|
||||||
|
### 1. Provision AWS Resources
|
||||||
|
- **EC2 Instance**: Launch a `t3.medium` (or larger) instance running Ubuntu 24.04 LTS.
|
||||||
|
- **Elastic IP**: Allocate a static Elastic IP and associate it with the EC2 instance. (Note the IP address).
|
||||||
|
- **Security Group**: Configure the security group to allow inbound traffic on:
|
||||||
|
* Port `25` (SMTP)
|
||||||
|
* Port `143` / `993` (IMAP / IMAPS)
|
||||||
|
* Port `587` / `465` (Submission / SMTPS)
|
||||||
|
* Port `80` / `443` (HTTP / HTTPS for Webmail & SSL certificates)
|
||||||
|
|
||||||
|
### 2. Request Port 25 Unblocking & Reverse DNS (PTR)
|
||||||
|
You cannot send emails from EC2 without removing the default Port 25 limit.
|
||||||
|
- **Action**: Submit the [Request to Remove Sending Limitations](https://aws.amazon.com/premiumsupport/knowledge-center/ec2-port-25-throttle/) form in the AWS Support Center.
|
||||||
|
- **Provide the following details**:
|
||||||
|
* Your Elastic IP address.
|
||||||
|
* Your email domain (e.g., `yourdomain.com`).
|
||||||
|
* The Reverse DNS (PTR) record you want associated with the IP (e.g., `mail.yourdomain.com`).
|
||||||
|
* A clear explanation of your use case (e.g., "Hosting our company email service for internal staff communications").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Development & Infrastructure Setup
|
||||||
|
|
||||||
|
Once AWS approves your request, proceed with the software installation.
|
||||||
|
|
||||||
|
### 1. Host Setup
|
||||||
|
- Install Docker and Docker Compose on the EC2 instance.
|
||||||
|
- Set up a swap file if memory usage is tight (especially if using Mailcow).
|
||||||
|
|
||||||
|
### 2. Mail Server Installation (Example: Mailcow)
|
||||||
|
- Clone the repository: `git clone https://github.com/mailcow/mailcow-dockerized`
|
||||||
|
- Generate configuration: `./generate_config.sh`
|
||||||
|
* Enter your mail subdomain (e.g., `mail.yourdomain.com`).
|
||||||
|
- Adjust configurations in `mailcow.conf` (e.g., timezone, HTTP/S ports).
|
||||||
|
- Spin up the containers: `docker compose up -d`
|
||||||
|
|
||||||
|
### 3. DNS Configuration
|
||||||
|
Add the following records at your Domain Registrar or DNS manager (Route 53, Cloudflare, etc.):
|
||||||
|
|
||||||
|
| Record Type | Host / Name | Value | Purpose |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **A** | `mail` | `<Your EC2 Elastic IP>` | Points to your mail server |
|
||||||
|
| **MX** | `@` (Root) | `10 mail.yourdomain.com.` | Directs incoming mail to the mail server |
|
||||||
|
| **TXT** | `@` | `v=spf1 ip4:<Your Elastic IP> -all` | SPF: Authorizes EC2 to send mail |
|
||||||
|
| **TXT** | `dkim._domainkey` | *Generated by Mailcow UI* | DKIM: Cryptographic mail signing |
|
||||||
|
| **TXT** | `_dmarc` | `v=DMARC1; p=quarantine; pct=100;` | DMARC: Action policy for failures |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Testing & Verification Plan
|
||||||
|
|
||||||
|
Run these verification checks before onboarding your organization.
|
||||||
|
|
||||||
|
### 1. Inbound Connection Port Check
|
||||||
|
Verify that the firewall/security groups are open to the public internet:
|
||||||
|
```bash
|
||||||
|
# From a remote machine, test SMTP
|
||||||
|
nc -zv <Your Elastic IP> 25
|
||||||
|
|
||||||
|
# Test IMAPS
|
||||||
|
nc -zv <Your Elastic IP> 993
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Inbound & Outbound Email Delivery
|
||||||
|
* **Outbound Test**: Create an inbox in your new mail server and send an email to an external address (e.g., a personal Gmail or Outlook account). Verify it arrives and does not go to spam.
|
||||||
|
* **Inbound Test**: Reply to that email from Gmail/Outlook and verify it is received on your self-hosted server.
|
||||||
|
|
||||||
|
### 3. Comprehensive Deliverability & Trust Test
|
||||||
|
* Use a service like [Mail-Tester](https://www.mail-tester.com/) or [MxToolbox](https://mxtoolbox.com/).
|
||||||
|
* Send a test email from your mail server to the address provided by Mail-Tester.
|
||||||
|
* **Target Score**: 10/10. Ensure SPF, DKIM, DMARC, and Reverse DNS (PTR) alignment are all marked green.
|
||||||
Loading…
Reference in a new issue