magic-pocket-cli 0.2.2__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/PKG-INFO +2 -2
  2. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/mediator.py +49 -1
  3. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/cloudformation.py +3 -0
  4. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/cloudfront.py +32 -1
  5. magic_pocket_cli-0.4.0/pocket_cli/templates/cloudformation/cf_function_api_host.js +9 -0
  6. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +8 -0
  7. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pyproject.toml +2 -2
  8. magic_pocket_cli-0.2.2/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -5
  9. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/.gitignore +0 -0
  10. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/__init__.py +0 -0
  11. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/__init__.py +0 -0
  12. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/aws_auth.py +0 -0
  13. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/awscontainer_cli.py +0 -0
  14. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
  15. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
  16. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
  17. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/deploy_cli.py +0 -0
  18. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/destroy_cli.py +0 -0
  19. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/dsql_cli.py +0 -0
  20. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/main_cli.py +0 -0
  21. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/migrate_cli.py +0 -0
  22. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/neon_cli.py +0 -0
  23. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/permissions_cli.py +0 -0
  24. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/rds_cli.py +0 -0
  25. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
  26. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/s3_cli.py +0 -0
  27. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/status_cli.py +0 -0
  28. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/tidb_cli.py +0 -0
  29. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/vpc_cli.py +0 -0
  30. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/cli/waf_cli.py +0 -0
  31. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/django_cli.py +0 -0
  32. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/__init__.py +0 -0
  33. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/__init__.py +0 -0
  34. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/builders/__init__.py +0 -0
  35. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/builders/codebuild.py +0 -0
  36. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
  37. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
  38. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
  39. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/ecr.py +0 -0
  40. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/efs.py +0 -0
  41. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
  42. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
  43. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/aws/state.py +0 -0
  44. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/awscontainer.py +0 -0
  45. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
  46. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
  47. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
  48. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/dsql.py +0 -0
  49. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/neon.py +0 -0
  50. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/rds.py +0 -0
  51. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/s3.py +0 -0
  52. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/tidb.py +0 -0
  53. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/upstash.py +0 -0
  54. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/resources/vpc.py +0 -0
  55. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/awscontainer.yaml +0 -0
  56. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
  57. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
  58. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
  59. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
  60. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
  61. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
  62. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
  63. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/init/django-settings.py +0 -0
  64. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
  65. {magic_pocket_cli-0.2.2 → magic_pocket_cli-0.4.0}/pocket_cli/templates/init/pocket_simple.toml +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magic-pocket-cli
3
- Version: 0.2.2
3
+ Version: 0.4.0
4
4
  Summary: CLI and deploy tools for magic-pocket.
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: awscrt>=0.19.0
7
7
  Requires-Dist: click>=8.1.7
8
8
  Requires-Dist: deepdiff>=6.7.1
9
9
  Requires-Dist: jinja2>=3.1.3
10
- Requires-Dist: magic-pocket>=0.2.2
10
+ Requires-Dist: magic-pocket>=0.4.0
11
11
  Requires-Dist: pathspec>=1.0.4
12
12
  Requires-Dist: python-on-whales>=0.68.0
13
13
  Requires-Dist: pyyaml>=6.0.1
@@ -60,6 +60,7 @@ class Mediator:
60
60
  def ensure_pocket_managed_secrets(self):
61
61
  self.create_pocket_managed_secrets(exists="ignore")
62
62
  self._cleanup_orphaned_secrets()
63
+ self.verify_user_stored_secrets()
63
64
  if self.context.awscontainer and self.context.awscontainer.secrets:
64
65
  sc = self.context.awscontainer.secrets
65
66
  if hasattr(sc, "pocket_store"):
@@ -69,6 +70,53 @@ class Mediator:
69
70
  if hasattr(sc, "allowed_ssm_resources"):
70
71
  del sc.allowed_ssm_resources
71
72
 
73
+ def verify_user_stored_secrets(self):
74
+ """type 付き user secret (stored mode) が deploy 前に provision 済みか検証する。
75
+
76
+ computed (managed) と違い pocket は値を生成しないため、未 provision でも
77
+ deploy は通り runtime まで遅延して落ちる。それを避けるため deploy 時に store を
78
+ 引いて存在を確認し、無ければ正準名を示して止める。管理 API は叩かない。
79
+ """
80
+ if self.context.awscontainer is None:
81
+ return
82
+ if (sc := self.context.awscontainer.secrets) is None:
83
+ return
84
+ missing: list[str] = []
85
+ for key, spec in sc.user.items():
86
+ # type 付き = stored mode のみ対象。name は from_settings で導出済み。
87
+ if spec.type is None or spec.name is None:
88
+ continue
89
+ store = spec.store or sc.store
90
+ if not self._stored_secret_exists(spec.name, store, sc.region):
91
+ missing.append(
92
+ " - %s (type=%s, store=%s): %s"
93
+ % (key, spec.type, store, spec.name)
94
+ )
95
+ if missing:
96
+ raise RuntimeError(
97
+ "stored mode の user secret が見つかりません。"
98
+ "deploy 前に下記の secret を provision してください "
99
+ "(値は接続 URL):\n" + "\n".join(missing)
100
+ )
101
+
102
+ def _stored_secret_exists(self, name: str, store: str, region: str) -> bool:
103
+ import boto3
104
+ from botocore.exceptions import ClientError
105
+
106
+ try:
107
+ if store == "ssm":
108
+ boto3.client("ssm", region_name=region).get_parameter(Name=name)
109
+ else:
110
+ boto3.client("secretsmanager", region_name=region).describe_secret(
111
+ SecretId=name
112
+ )
113
+ return True
114
+ except ClientError as e:
115
+ code = e.response.get("Error", {}).get("Code", "")
116
+ if code in ("ParameterNotFound", "ResourceNotFoundException"):
117
+ return False
118
+ raise
119
+
72
120
  def _cleanup_orphaned_secrets(self):
73
121
  """SSM/SM にあるが managed 定義にないシークレットを削除する"""
74
122
  if self.context.awscontainer is None:
@@ -109,7 +157,7 @@ class Mediator:
109
157
  return self._generate_rsa_pem()
110
158
  elif spec.type == "cloudfront_signing_key":
111
159
  return self._generate_rsa_pem()
112
- elif spec.type == "spa_token_secret":
160
+ elif spec.type in ("spa_token_secret", "origin_verify_secret"):
113
161
  return secrets.token_hex(32)
114
162
  else:
115
163
  raise RuntimeError("Unknown secret type: %s" % spec.type)
@@ -358,8 +358,10 @@ class CloudFrontStack(Stack):
358
358
  self,
359
359
  context: CloudFrontContext,
360
360
  token_secret_value: str = "",
361
+ origin_verify_secret_value: str = "",
361
362
  ):
362
363
  self._token_secret_value = token_secret_value
364
+ self._origin_verify_secret_value = origin_verify_secret_value
363
365
  super().__init__(context)
364
366
 
365
367
  def get_client(self):
@@ -544,6 +546,7 @@ class CloudFrontStack(Stack):
544
546
  deploy_hash_function_codes=deploy_hash_function_codes,
545
547
  api_host_function_code=api_host_function_code,
546
548
  has_token_kvs=self._has_token_kvs,
549
+ origin_verify_secret=self._origin_verify_secret_value,
547
550
  **context_data,
548
551
  )
549
552
  return "\n".join(
@@ -49,6 +49,7 @@ class CloudFront:
49
49
  self.s3_client = boto3.client("s3", region_name=context.s3_region)
50
50
  self.cf_client = boto3.client("cloudfront")
51
51
  self._token_secret_value: str = ""
52
+ self._origin_verify_secret_value: str = ""
52
53
 
53
54
  @property
54
55
  def description(self):
@@ -71,7 +72,9 @@ class CloudFront:
71
72
  @property
72
73
  def stack(self):
73
74
  return CloudFrontStack(
74
- self.context, token_secret_value=self._token_secret_value
75
+ self.context,
76
+ token_secret_value=self._token_secret_value,
77
+ origin_verify_secret_value=self._origin_verify_secret_value,
75
78
  )
76
79
 
77
80
  def create(self, mediator: Mediator | None = None):
@@ -79,6 +82,7 @@ class CloudFront:
79
82
 
80
83
  def update(self, mediator: Mediator | None = None):
81
84
  self._prepare_token_secret(mediator)
85
+ self._prepare_origin_verify_secret(mediator)
82
86
  self._ensure_redirect_from()
83
87
  if not self.stack.exists:
84
88
  self.stack.create()
@@ -136,6 +140,33 @@ class CloudFront:
136
140
  % self.context.token_secret
137
141
  )
138
142
 
143
+ def _prepare_origin_verify_secret(self, mediator: Mediator | None):
144
+ """enable_origin_verify 時に origin verify secret の値を store から読む。
145
+
146
+ 値は CFn テンプレートの OriginCustomHeaders に焼き込まれる
147
+ (token_secret の post-deploy KVS とは異なり、distribution config の一部
148
+ なので create/update 時点で必要)。managed secret なので AwsContainer
149
+ deploy 時に既に生成済み (get_resources は AwsContainer → CloudFront 順)。
150
+ """
151
+ from pocket.context import ORIGIN_VERIFY_SECRET_KEY
152
+
153
+ if not self.context.enable_origin_verify:
154
+ return
155
+ if not mediator:
156
+ return
157
+ ac = mediator.context.awscontainer
158
+ if not ac or not ac.secrets:
159
+ return
160
+ secrets = ac.secrets.pocket_store.secrets
161
+ value = secrets.get(ORIGIN_VERIFY_SECRET_KEY)
162
+ if isinstance(value, str):
163
+ self._origin_verify_secret_value = value
164
+ else:
165
+ echo.warning(
166
+ "origin verify secret '%s' が managed secrets に見つかりません"
167
+ % ORIGIN_VERIFY_SECRET_KEY
168
+ )
169
+
139
170
  def _write_token_secret_to_kvs(self):
140
171
  if not self._token_secret_value:
141
172
  return
@@ -0,0 +1,9 @@
1
+ function handler(event) {
2
+ var request = event.request;
3
+ request.headers['x-forwarded-host'] = { value: request.headers.host.value };
4
+ // event.viewer.ip は CloudFront が TCP 接続から取得する viewer IP で詐称不可。
5
+ // viewer が同名 header を送っても上書きするため、origin は真の client IP を
6
+ // 信頼できる。OriginVerifyMiddleware が REMOTE_ADDR に正規化する。
7
+ request.headers['x-pocket-viewer-ip'] = { value: event.viewer.ip };
8
+ return request;
9
+ }
@@ -129,6 +129,14 @@ Resources:
129
129
  CustomOriginConfig:
130
130
  OriginProtocolPolicy: https-only
131
131
  HTTPSPort: 443
132
+ # {% if origin_verify_secret %}
133
+ # CloudFront → origin にのみ付与する secret header。viewer が同名 header
134
+ # を送っても CloudFront が上書きするため詐称不可。origin (API GW / Lambda)
135
+ # 直叩きを検出するために OriginVerifyMiddleware が同値を検証する。
136
+ OriginCustomHeaders:
137
+ - HeaderName: "X-Pocket-Origin-Verify"
138
+ HeaderValue: "{{ origin_verify_secret }}"
139
+ # {% endif %}
132
140
  # {% endfor %}
133
141
  # {% if managed_asset_files %}
134
142
  - Id: "{{ resource_prefix }}pocket-managed"
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "magic-pocket-cli"
3
- version = "0.2.2"
3
+ version = "0.4.0"
4
4
  description = "CLI and deploy tools for magic-pocket."
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
7
- "magic-pocket>=0.2.2",
7
+ "magic-pocket>=0.4.0",
8
8
  "click>=8.1.7",
9
9
  "jinja2>=3.1.3",
10
10
  "python-on-whales>=0.68.0",
@@ -1,5 +0,0 @@
1
- function handler(event) {
2
- var request = event.request;
3
- request.headers['x-forwarded-host'] = { value: request.headers.host.value };
4
- return request;
5
- }