mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 11:23:12 +00:00
Migrate secrets from SSM Parameter Store to Secrets Manager
- Slack bot token and signing secret now read from Secrets Manager - 3CX credentials (domain, client-id, client-secret) moved to Secrets Manager under afterhours-shift-manager/3cx-* prefix - Channel ID is now a non-secret CloudFormation parameter (ShiftChannel) - Add shared secrets.py helper for Secrets Manager reads - Remove SSM and KMS IAM policies, add secretsmanager:GetSecretValue
This commit is contained in:
parent
0fed60248a
commit
7979e2a4e7
5 changed files with 43 additions and 73 deletions
|
|
@ -10,31 +10,18 @@ import os
|
|||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import boto3
|
||||
|
||||
from shared.three_cx_client import ThreeCXClient
|
||||
from shared.schedule import ShiftSchedule
|
||||
from shared.secrets import get_secret
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
EASTERN = ZoneInfo("America/New_York")
|
||||
|
||||
# System extensions to exclude from roster sync
|
||||
EXCLUDE_NAMES = {"Voicemail", "IVR", "Fax"}
|
||||
|
||||
|
||||
def get_3cx_credentials() -> dict:
|
||||
ssm = boto3.client("ssm")
|
||||
prefix = os.environ.get("TCX_SSM_PREFIX", "/3cx-scheduler")
|
||||
params = ssm.get_parameters_by_path(Path=prefix, WithDecryption=True)
|
||||
creds = {}
|
||||
for p in params["Parameters"]:
|
||||
key = p["Name"].split("/")[-1]
|
||||
creds[key] = p["Value"]
|
||||
return creds
|
||||
|
||||
|
||||
def handler(event, context):
|
||||
now = datetime.now(EASTERN)
|
||||
|
||||
|
|
@ -52,12 +39,12 @@ def handler(event, context):
|
|||
"Starting roster sync from 3CX group '%s' at %s", group_name, now.isoformat()
|
||||
)
|
||||
|
||||
creds = get_3cx_credentials()
|
||||
secret_prefix = os.environ["TCX_SECRET_PREFIX"]
|
||||
client = ThreeCXClient(
|
||||
domain=creds["domain"],
|
||||
domain=get_secret(f"{secret_prefix}domain"),
|
||||
auth_mode="oauth",
|
||||
client_id=creds["client_id"],
|
||||
client_secret=creds["client_secret"],
|
||||
client_id=get_secret(f"{secret_prefix}client-id"),
|
||||
client_secret=get_secret(f"{secret_prefix}client-secret"),
|
||||
)
|
||||
|
||||
members = client.get_group_members(group_name)
|
||||
|
|
|
|||
16
src/shared/python/shared/secrets.py
Normal file
16
src/shared/python/shared/secrets.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"""Fetch secrets from AWS Secrets Manager."""
|
||||
|
||||
import boto3
|
||||
|
||||
_client = None
|
||||
|
||||
|
||||
def _get_client():
|
||||
global _client
|
||||
if _client is None:
|
||||
_client = boto3.client("secretsmanager")
|
||||
return _client
|
||||
|
||||
|
||||
def get_secret(secret_id: str) -> str:
|
||||
return _get_client().get_secret_value(SecretId=secret_id)["SecretString"]
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
import logging
|
||||
import os
|
||||
|
||||
import boto3
|
||||
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
|
||||
|
||||
from app import create_app
|
||||
from shared.secrets import get_secret
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
|
@ -16,23 +16,15 @@ logging.basicConfig(
|
|||
format="%(asctime)s %(levelname)s %(name)s: %(message)s", level=logging.INFO
|
||||
)
|
||||
|
||||
# Lazy-initialized app singleton
|
||||
_slack_handler = None
|
||||
|
||||
|
||||
def _get_handler() -> SlackRequestHandler:
|
||||
global _slack_handler
|
||||
if _slack_handler is None:
|
||||
ssm = boto3.client("ssm")
|
||||
bot_token = ssm.get_parameter(
|
||||
Name=os.environ["SLACK_BOT_TOKEN_PARAM"], WithDecryption=True
|
||||
)["Parameter"]["Value"]
|
||||
signing_secret = ssm.get_parameter(
|
||||
Name=os.environ["SLACK_SIGNING_SECRET_PARAM"], WithDecryption=True
|
||||
)["Parameter"]["Value"]
|
||||
schedule_channel = ssm.get_parameter(
|
||||
Name=os.environ["SHIFT_CHANNEL_PARAM"], WithDecryption=True
|
||||
)["Parameter"]["Value"]
|
||||
bot_token = get_secret(os.environ["SLACK_BOT_TOKEN_SECRET"])
|
||||
signing_secret = get_secret(os.environ["SLACK_SIGNING_SECRET"])
|
||||
schedule_channel = os.environ["SHIFT_CHANNEL"]
|
||||
|
||||
app = create_app(bot_token, signing_secret, schedule_channel=schedule_channel)
|
||||
_slack_handler = SlackRequestHandler(app=app)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from slack_sdk import WebClient
|
|||
|
||||
from shared.blocks import build_pay_summary_blocks, build_week_schedule
|
||||
from shared.schedule import FALLBACK_EXTENSION, ShiftSchedule
|
||||
from shared.secrets import get_secret
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
|
@ -147,13 +148,8 @@ def handler(event, context):
|
|||
)
|
||||
return {"skipped": True}
|
||||
|
||||
ssm = boto3.client("ssm")
|
||||
bot_token = ssm.get_parameter(
|
||||
Name=os.environ["SLACK_BOT_TOKEN_PARAM"], WithDecryption=True
|
||||
)["Parameter"]["Value"]
|
||||
channel_id = ssm.get_parameter(
|
||||
Name=os.environ["SHIFT_CHANNEL_PARAM"], WithDecryption=True
|
||||
)["Parameter"]["Value"]
|
||||
bot_token = get_secret(os.environ["SLACK_BOT_TOKEN_SECRET"])
|
||||
channel_id = os.environ["SHIFT_CHANNEL"]
|
||||
|
||||
schedule = ShiftSchedule()
|
||||
slack = WebClient(token=bot_token)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ Parameters:
|
|||
Timezone:
|
||||
Type: String
|
||||
Default: "America/New_York"
|
||||
ShiftChannel:
|
||||
Type: String
|
||||
Description: Slack channel ID for schedule posts and shift notifications
|
||||
SchedulerFunctionName:
|
||||
Type: String
|
||||
Default: "3cx-ring-group-scheduler"
|
||||
|
|
@ -63,9 +66,9 @@ Resources:
|
|||
Environment:
|
||||
Variables:
|
||||
SHIFT_TABLE: !Ref ShiftTable
|
||||
SLACK_BOT_TOKEN_PARAM: /afterhours-shift-manager/slack-bot-token
|
||||
SLACK_SIGNING_SECRET_PARAM: /afterhours-shift-manager/slack-signing-secret
|
||||
SHIFT_CHANNEL_PARAM: /afterhours-shift-manager/channel-id
|
||||
SLACK_BOT_TOKEN_SECRET: afterhours-shift-manager/slack-bot-token
|
||||
SLACK_SIGNING_SECRET: afterhours-shift-manager/slack-signing-secret
|
||||
SHIFT_CHANNEL: !Ref ShiftChannel
|
||||
SCHEDULER_FUNCTION_NAME: !Ref SchedulerFunctionName
|
||||
TZ: !Ref Timezone
|
||||
Policies:
|
||||
|
|
@ -74,16 +77,9 @@ Resources:
|
|||
- Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParameter
|
||||
- secretsmanager:GetSecretValue
|
||||
Resource:
|
||||
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/afterhours-shift-manager/*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- kms:Decrypt
|
||||
Resource: "*"
|
||||
Condition:
|
||||
StringEquals:
|
||||
"kms:ViaService": !Sub "ssm.${AWS::Region}.amazonaws.com"
|
||||
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- lambda:InvokeFunction
|
||||
|
|
@ -108,8 +104,8 @@ Resources:
|
|||
Environment:
|
||||
Variables:
|
||||
SHIFT_TABLE: !Ref ShiftTable
|
||||
SLACK_BOT_TOKEN_PARAM: /afterhours-shift-manager/slack-bot-token
|
||||
SHIFT_CHANNEL_PARAM: /afterhours-shift-manager/channel-id
|
||||
SLACK_BOT_TOKEN_SECRET: afterhours-shift-manager/slack-bot-token
|
||||
SHIFT_CHANNEL: !Ref ShiftChannel
|
||||
SES_SENDER: noreply@seahaven.com
|
||||
PAYROLL_RECIPIENTS: payroll@seahaven.com
|
||||
PAY_REPORT_USER: U0A3SC48T47
|
||||
|
|
@ -120,16 +116,9 @@ Resources:
|
|||
- Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParameter
|
||||
- secretsmanager:GetSecretValue
|
||||
Resource:
|
||||
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/afterhours-shift-manager/*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- kms:Decrypt
|
||||
Resource: "*"
|
||||
Condition:
|
||||
StringEquals:
|
||||
"kms:ViaService": !Sub "ssm.${AWS::Region}.amazonaws.com"
|
||||
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ses:SendEmail
|
||||
|
|
@ -164,7 +153,7 @@ Resources:
|
|||
Environment:
|
||||
Variables:
|
||||
SHIFT_TABLE: !Ref ShiftTable
|
||||
TCX_SSM_PREFIX: /3cx-scheduler
|
||||
TCX_SECRET_PREFIX: afterhours-shift-manager/3cx-
|
||||
SYNC_GROUP: DEFAULT
|
||||
TZ: !Ref Timezone
|
||||
Policies:
|
||||
|
|
@ -173,19 +162,9 @@ Resources:
|
|||
- Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- ssm:GetParametersByPath
|
||||
- ssm:GetParameter
|
||||
- ssm:GetParameters
|
||||
- secretsmanager:GetSecretValue
|
||||
Resource:
|
||||
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/3cx-scheduler"
|
||||
- !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/3cx-scheduler/*"
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- kms:Decrypt
|
||||
Resource: "*"
|
||||
Condition:
|
||||
StringEquals:
|
||||
"kms:ViaService": !Sub "ssm.${AWS::Region}.amazonaws.com"
|
||||
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
||||
Events:
|
||||
# Daily at 6am ET (before the 7am schedule post and 8am 3CX scheduler)
|
||||
# EST: 6am ET = 11:00 UTC (Nov-Mar)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue