6ã¶æãšããçãæéã§ã¯ãããŸãããããšã³ãžãã¢3幎ç®ã«ããŠæ°èŠSaaSãããžã§ã¯ãã®ã€ã³ãã©ããããã¯ãšã³ããŸã§ã®èšèšã»å®è£ ãä»»ãããŸããã
MVPïŒMinimum Viable ProductïŒãã§ãŒãºããã®ã¹ã¿ãŒããšããããšããããã³ã¹ããéçšè² è·ããªãœãŒã¹å¹çã®èгç¹ãéèŠããåããã³ãã§åããªãœãŒã¹ãå
±æãããããŒã«ã¢ãã«ãã軞ã«ã¢ãŒããã¯ãã£ãæ€èšãå§ããŸããã
ããããããŒã«ã¢ãã«ã«ãããŠçµ¶å¯Ÿã«èš±ãããªãã®ãä»ããã³ããžã®ããŒã¿æ··å
¥ã§ããèšèšã®åœåãããããã劥åãããçµããããšããã©ã€ã³ã¯æç¢ºã§ããã
èšèšã«ããããOâReillyã®æžç±ããã«ãããã³ãSaaSã¢ãŒããã¯ãã£ã®æ§ç¯âååããã¹ããã©ã¯ãã£ã¹ãAWSã¢ãŒããã¯ãã£ãã¿ãŒã³ãããAWSãæäŸãããªãŒãã³ãœãŒã¹ã®SaaS Builder Toolkit for AWSïŒSBTïŒãªã©ãåŠç¿ãå®éã«æ€èšŒãªã©ãè¡ããŸãããããã§ç€ºãããŠããè«çåé¢ã®æŠå¿µã¯ãšãŠãæŽç·ŽãããŠããŸãããããããžã§ã¯ãã®èŠä»¶ã«ãã®ãŸãŸåœãŠã¯ããã«ã¯ããã€ãã®ãã¬ãŒããªããååšããŸããã
ããã§ãAWSã®ãªãã¡ã¬ã³ã¹ããæŠå¿µãªã©ãåèã«ãã€ã€ããããžã§ã¯ãã®èŠä»¶ã«åãããç¬èªã®ãã«ãµãŒããŒã¬ã¹ã»ã¢ãŒããã¯ãã£ãæ§ç¯ããã¢ãããŒãããšããŸãããçµæãšããŠãèªèšŒããããŒã¿ããŒã¹ã®è¡ã¬ãã«ã«è³ããŸã§ãè€æ°ã®ã¬ã€ã€ãŒã§æ®µéçã«ã¢ã¯ã»ã¹ãå¶éããããŒã¿åé¢ã確å®ã«è¡ãå ç¢ãªã·ã¹ãã ã宿ããŸããã
æ¬çš¿ã§ã¯ãç§ãã¡ãæ§ç¯ããããŒã¿åé¢ã®å®è£ ã¢ãããŒãã«ã€ããŠè§£èª¬ããŸãã
â æ¬èšäºã«ãããäž»ãªäœ¿çšæè¡ã»AWSãµãŒãã¹
* ã€ã³ãã©ã¹ãã©ã¯ãã£ïŒIaCïŒ: AWS CDK (TypeScript)
* èªèšŒã»èªå¯: Amazon Cognito, AWS IAM (STS, ABAC)
* ã³ã³ãã¥ãŒãã£ã³ã°: AWS Lambda, Amazon API Gateway
* ããŒã¿ããŒã¹ã»ã¹ãã¬ãŒãž: Amazon Aurora PostgreSQL (Data API), Amazon DynamoDB, Amazon S3
1. AWSãªãã¡ã¬ã³ã¹ããã®åŠã³ãšãç¬èªå®è£ ãžã®å€æ
AWSã®ãªãã¡ã¬ã³ã¹ã§ç¹ã«åèã«ãªã£ãã®ã¯ãã·ã¹ãã ãControl PlaneïŒç®¡çé åïŒãšApplication PlaneïŒã¢ããªå®è¡é åïŒã«è«çåé¢ãããšããèãæ¹ã§ãããã®æ§æã¯ãæçµçãªCDKã®ã€ã³ãã©èšèšã«ãåãå ¥ããŠããŸãã
åºå žïŒSaaS Architecture Fundamentals
äžæ¹ã§ãæšæºçãªSaaSåãã©ã€ãã©ãªïŒSBTãªã©ïŒããã®ãŸãŸæ¡çšãããç¬èªã«å®è£ ãè¡ã£ãã®ã«ã¯æç¢ºãªçç±ããããŸãã
- ãã©ãã¯ããã¯ã¹åã®åé¿ãšã³ã³ãããŒã«ã®æ ä¿: ããŒã¿åé¢ãšããã·ã¹ãã ã®æãã¯ãªãã£ã«ã«ãªéšåãå€éšã©ã€ãã©ãªã®é èœãããåŠçã«äŸåãããèªåãã¡ã§å®å šã«ã³ã³ãããŒã«ã§ããç¶æ ãä¿ã¡ããã£ãã
- ã·ã³ãã«ãªããŒã«ã¢ãã«ãžã®æé©å: ãªãã¡ã¬ã³ã¹ã§ã¯ããã³ãè¿œå æã«éåæã®åçããããžã§ãã³ã°ãè¡ãæ§æãäžè¬çã§ãããå šããã³ãããªãœãŒã¹ãå ±æããã·ã³ãã«ãªããŒã«ã¢ãã«ã§ã¯ããã®ãªãŒããŒãããã¯äžèŠãšå€æããŸããã
- è€éãªéå±€ããé¢é£ãæã€ããŒã¿ã¢ãã«ãžã®å¯Ÿå¿: NoSQLãäžå¿ãšãããµã³ãã«ãå€ãäžããããžã§ã¯ãã®ãã¡ã€ã³ã¯è€éãªé¢é£ãæã€ãããAurora PostgreSQLã䞻軞ã«çœ®ãå¿ èŠããããŸããã
ãããã®èŠä»¶ãæºãããããã¢ãŒããã¯ãã£ã®éªšæ Œã¯AWSã®ææ³ãèžè¥²ãã€ã€ãå éšã®å®è£ ã¯å®å šã«ã³ã³ãããŒã«å¯èœãªç¬èªæ§æã§çµã¿äžããŸããã
2. 4ã¬ã€ã€ãŒã«ãããè«ççãªããŒã¿åé¢ã®å®è£
ããŒã¿æŒæŽ©ã®ãªã¹ã¯ãæå°éã«æãããããåäžã®é²åŸ¡çã«é Œãã®ã§ã¯ãªããåã¬ã€ã€ãŒã§ç¢ºå®ã«ã¢ã¯ã»ã¹ãå¶éããå€å±€çãªèšèšãè¡ããŸããã

â ãèªèšŒå±€ãCognito JWT ãžã®ããã³ãã³ã³ããã¹ãæ³šå ¥
ãŸãèªèšŒã®æç¹ã§ãããã³ãIDããæ¹ããäžå¯èœãªç¶æ ãã§JWTã«å°ã蟌ããŸãã
Cognito User Poolã®ã«ã¹ã¿ã 屿§ custom:tenantId ã mutable: falseïŒæžãæãäžå¯ïŒã§å®çŸ©ãããã°ã€ã³æã«èµ·åãã Pre-Token Generation V2 Lambda ã§DBããããã³ãæ
å ±ãååŸããŠIDããŒã¯ã³ãžæ³šå
¥ããŸãã
// ã«ã¹ã¿ã 屿§å®çŸ©ïŒãŠãŒã¶ãŒèªèº«ã«ããæžãæããçŠæ¢
customAttributes: {
tenantId: new cognito.StringAttribute({ mutable: false }),
role: new cognito.StringAttribute({ mutable: true }),
},
// Pre-Token Generation V2 ããªã¬ãŒãã¢ã¿ãã
this.userPool.addTrigger(
cognito.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
preTokenFn,
cognito.LambdaVersion.V2_0,
);
ãã®Lambdaå ã§ã¯ããã©ã³ã¶ã¯ã·ã§ã³ãéå§ããŠRLSã³ã³ããã¹ããã»ããããããã§DBã¯ãšãªãå®è¡ããæå±æ å ±ãªã©ãIDããŒã¯ã³ãžåçã«æžã蟌ã¿ãŸããããã«ããã以éã®ãã¹ãŠã®APIãªã¯ãšã¹ãã«ãããŠãããŒã¯ã³1æ¬ããå®å šã«ããã³ãã³ã³ããã¹ãã埩å å¯èœã«ãªããŸãã
â¡ ãã³ã³ãã¥ãŒãã£ã³ã°å±€ãLambda Tenant Isolation Mode à API Gateway
ããŒã«ã¢ãã«ã§é ãæ©ãŸããã®ããLambdaã®ãŠã©ãŒã ã³ã³ããåå©çšã«ããã°ããŒãã«å€æ°ã®ç¶æ ãªãŒã¯ãªã¹ã¯ã§ãããã®åé¡ã«å¯ŸããŠãAWS LambdaãæäŸããTenant Isolation Modeãæ¡çšããŸããã
// CDK: ããã³ãåé¢ã¢ãŒãã®æå¹å
this.apiLambda = createManagedNodejsFunction(this, 'ApiFunction', {
tenancyConfig: lambda.TenancyConfig.PER_TENANT, // â
ããã³ãããšã«å®è¡ã³ã³ãããåé¢
});
LambdaåŽã§å¿
èŠãªã®ã¯ãã®1è¡ã ãã§ãããã ããLambdaåäœã§ã¯ãã©ã®ããã³ãã®ãªã¯ãšã¹ããããå€å¥ã§ããªããããAPI Gatewayã®çµ±åèšå®ã§ãCognito Authorizerãæ€èšŒããJWTã¯ã¬ãŒã ã X-Amz-Tenant-Id ããããŒãšããŠåŒ·å¶æ³šå
¥ããŸãã
// API Gateway Integration: JWTã¯ã¬ãŒã ãããããŒã«ãããã³ã°
new apigw.LambdaIntegration(this.apiLambda, {
requestParameters: {
// Cognitoãæ€èšŒããå€ã®ã¿ãLambdaã«æž¡ãïŒã¯ã©ã€ã¢ã³ãããåœè£
äžå¯ïŒ
'integration.request.header.X-Amz-Tenant-Id':
'context.authorizer.claims.custom:tenantId',
},
});
ã¯ã©ã€ã¢ã³ãåŽãåããããŒãèªåã§æå®ããŠããAPI Gatewayãäžæžãããããåœè£ ã¯æç«ããŸããããã®é£æºã«æ°ã¥ããŸã§ã«å°ãæéãããããŸãããããCDKæ°è¡ã§å®è¡ç°å¢ã¬ãã«ã®å®å šãªéé¢ãæã«å ¥ãããšãã匷åãªã¢ãããŒãã§ãã
⢠ãIAMèªå¯å±€ãSTS ã»ãã·ã§ã³ã¿ã° à ABAC ã«ããS3ã»DynamoDBåé¢
ããã³ãã®ã¢ã»ããïŒS3ïŒããªã¢ã«ã¿ã€ã ç¶æ ïŒDynamoDBïŒã®åé¢ã«ã¯ãAWS IAMã®æ©èœãå©çšããŸããSTS AssumeRole æã«ããã³ãèå¥åãã»ãã·ã§ã³ã¿ã°ãšããŠä»äžããããšã§ãIAMããªã·ãŒå€æ°ããã®ã¿ã°ã«å±éãããããã³ãããšã®ããŒã¿ç¯å²ã®ã¿ã«æäœãå¶éããäžæèªèšŒæ å ±ãçºè¡ããŸãã
ããã³ãæ°ãå¢ããŠãIAMããŒã«ãããªã·ãŒã远å ããå¿ èŠããªããã¹ã±ãŒã©ãã«ãªABACïŒå±æ§ããŒã¹ã®ã¢ã¯ã»ã¹å¶åŸ¡ïŒæ¹åŒã§ãã
S3ã¢ã»ãããã±ããã§ã¯ãIAMããªã·ãŒå€æ° ${aws:PrincipalTag/tenantId} ã§ãã¹ãçµã蟌ã¿ãŸãã
// ãªãœãŒã¹ãã¹ãã»ãã·ã§ã³ã¿ã°ã§åçã«å¶é
resources: [bucket.arnForObjects('assets/${aws:PrincipalTag/tenantId}/*')],
// Trust Policy: tenantId ã¿ã°ãªãã® AssumeRole ã¯äžåæåŠ
conditions: {
'ForAllValues:StringEquals': { 'aws:TagKeys': ['tenantId'] },
Null: { 'aws:RequestTag/tenantId': 'false' }, // ã¿ã°å¿
é
},
DynamoDBã§ãåæ§ã« dynamodb:LeadingKeys æ¡ä»¶ãçšããããŒãã£ã·ã§ã³ããŒã®ãã¬ãã£ãã¯ã¹ãã»ãã·ã§ã³ã¿ã°ã®å€ã§å¶éããŠããŸãã
ã¢ããªåŽã§ã¯ãªã¯ãšã¹ãããšã«ããã³ãIDãã¿ã°ãšããŠä»äžããªããSTSãž AssumeRole ãåŒã³åºããçºè¡ãããäžæèªèšŒæ
å ±ïŒæå¹æé15åïŒã¯Lambdaã®å®è¡ã³ã³ããã¹ãå
ã§ãã£ãã·ã¥ããŸãã
Trust Policyã® Null æ¡ä»¶ãæ£ããèšè¿°ãããŸã§ã«äœåºŠãIAMã®ä»æ§ã«èŠãããããŸãããããã¿ã°ãå¿
ãååšããããšãPolicyåŽã§ä¿èšŒããããšããèãæ¹ãè
ã«èœã¡ãŠããã¯ãIAMã®ã»ãã¥ãªãã£ã¢ãã«ã®ç²Ÿç·»ãã«æéãåããŸããã
⣠ãRDBå±€ãPostgreSQL RLS ã«ãããã§ã€ã«ã»ãŒããªããŒã¿ä¿è·
ãªã¬ãŒã·ã§ãã«ããŒã¿ãžã®ã¢ã¯ã»ã¹ã«ãããæçµçãªç Šã¯ãPostgreSQLïŒAuroraïŒã®RLSïŒRow-Level SecurityïŒã§ãã
ä»åã¯LambdaããAuroraãžã®ã¢ã¯ã»ã¹ã«Data APIãæŽ»çšããŠããŸããData APIã®ã¹ããŒãã¬ã¹ãªæ¥ç¶ã«ãããŠRLSã確å®ã«æ©èœãããããããã©ã³ã¶ã¯ã·ã§ã³éå§æã«ããã³ãIDãã»ãã·ã§ã³ã«ãã€ã³ããã該åœããã³ãã®è¡ã®ã¿ãåç §ã»æŽæ°å¯èœã«ãããã£ã«ã¿ãããŒã¿ããŒã¹ãšã³ãžã³å éšã§åŒ·å¶ããŠããŸãã
ããã«ãç°åžžãªå€ïŒäžæ£ãªUUIDãªã©ïŒãæž¡ãããå Žåã¯ãšã©ãŒã«ããNULLãè¿ããRLSã«ãã£ãŠå®å šã«å šè¡ãžã®ã¢ã¯ã»ã¹ã鮿ãããã§ã€ã«ã»ãŒããªèšèšãæ¡çšããŸãããã¢ããªå±€ã§ã¯DBã¢ã¯ã»ã¹ãå ±éã®ã©ãããŒé¢æ°çµç±ã«åŒ·å¶ããŠãããããããžãã¹ããžãã¯å ã§ããã³ãæå®ãæžãå¿ããŠãããŒã¿æ··å ¥ãèµ·ããªãæ§é ã«ãªã£ãŠããŸãã
3. ä»åã®ã€ã³ãã©æ§ç¯ã§åŸãããæå¿ã
æ§ç¯ããã¢ãŒããã¯ãã£ããããããå ·äœçãªææã¯2ã€ã§ãã
- ããã³ãè¿œå æã®ãªãŒããŒãããæé€: IAMã®ã¿ã°ããŒã¹ïŒABACïŒãDBã®RLSãçšããè«çåé¢ã«å¯ããããšã§ãæ°èŠããã³ãè¿œå æã«å°çšã€ã³ãã©ããããã€ããåŸ ã¡æéããªããªããŸãããAPIã®åŒã³åºãäžã€ã§ç¬æã«ãªã³ããŒãã£ã³ã°ãå®äºãã身軜ãªéçšãå®çŸããŠããŸãã
- ã»ãã¥ãªãã£ã®èªç¥è² è·ã®è»œæž: è€æ°ã®ããŒã¿å颿©èœãã€ã³ãã©ã»å ±éããã«ãŠã§ã¢å±€ã§å®çµããŠãããããæ©èœéçºã®æ åœè ã¯ã»ãã¥ãªãã£ã®å®è£ 詳现ãæèããå¿ èŠããããŸãããå®è£ ãã¹ãæ§é çã«èµ·ãã«ããèšèšã«ãªã£ãŠããŸãã
4. ãŸãšã
AWSãæäŸãããã«ãããã³ãã®ãã¹ããã©ã¯ãã£ã¹ã¯ããããžã§ã¯ãã®èŠä»¶ã«åãããŠã¢ãŒããã¯ãã£ãæ€èšããããã®éåžžã«åèã«ãªããŸããã
Control PlaneãšApplication Planeãåé¢ããæ§æãåºæ¬ãšãã€ã€ãããã« Lambda Tenant Isolation Mode ã STS à IAM ABACããããŠData APIãæŽ»çšãã PostgreSQL RLS ãçµã¿åãããããšã§ãããžãã¹ããžãã¯ãæ±æããªã匷åºãªããŒã¿åé¢ãå®çŸã§ããŸããã
ãã¹ããã©ã¯ãã£ã¹ã®æŠå¿µãçè§£ãããã¬ãŒããªããè©äŸ¡ããŠèªåãã¡ã®èŠä»¶ã«åãããŠä»çµã¿ãèããçµéšã¯ãèšèšåãéããéåžžã«è¯ãæ©äŒã«ãªã£ããšæããŠããŸããåãããã«ãã«ãããã³ãã®èšèšã«åãåããšã³ãžãã¢ã®çããã®åèã«ãªãã°å¹žãã§ãã
åèæç®ã»ãªã³ã¯é
- æžç±: ããã«ãããã³ãSaaSã¢ãŒããã¯ãã£ã®æ§ç¯âååããã¹ããã©ã¯ãã£ã¹ãAWSã¢ãŒããã¯ãã£ãã¿ãŒã³ãïŒTod Golding è / æ²³åå²ä¹ã»æ«»è°·åºäºº èš³, OâReilly JapanïŒ
- AWSå ¬åŒãªãã¡ã¬ã³ã¹: SaaS Builder Toolkit for AWS (SBT) GitHub ãªããžããª
- AWS Well-Architected Framework: SaaS Lens
- AWS Lambda: AWS Lambda Tenant Isolation Mode ã®çºè¡š (Whatâs New)
- AWS IAM: ABACïŒå±æ§ããŒã¹ã®ã¢ã¯ã»ã¹å¶åŸ¡ïŒã®æŠèŠ
- ããŒã¿ããŒã¹å颿Šç¥: PostgreSQL Row-Level Security ã䜿çšãããã«ãããã³ãããŒã¿åé¢ (AWS Database Blog)
