1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2025-03-12 16:47:03 +01:00

Build and deploy support for AWS

Dieser Commit ist enthalten in:
Chase Douglas 2025-02-07 13:03:57 -08:00
Ursprung bdab7b49ee
Commit f4a40cba26
7 geänderte Dateien mit 715 neuen und 0 gelöschten Zeilen

49
.github/workflows/lambda.yml gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,49 @@
name: Build Lambda Package
on: workflow_dispatch
jobs:
build:
runs-on: ubuntu-24.04-arm
container:
image: public.ecr.aws/codebuild/amazonlinux2-aarch64-standard:3.0
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install development packages
run: sudo yum install -y krb5-devel openldap-devel
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install Cargo Lambda
uses: jaxxstorm/action-install-gh-release@v1.9.0
with:
repo: cargo-lambda/cargo-lambda
platform: linux
arch: aarch64
- name: Setup rust Cache
uses: Swatinem/rust-cache@v2
- name: Build with Cargo
run: cargo lambda build --verbose
- name: Copy libpq and its dependencies
run: cp /lib64/{libcrypt.so.2,liblber-2.4.so.2,libldap_r-2.4.so.2,libpq.so.5,libsasl2.so.3} target/lambda/vaultwarden/
# This ensures passes the startup checks for the web-vault, which is
# instead served statically from an S3 Bucket
- name: Create placeholder web-vault/index.html
run: |-
mkdir target/lambda/vaultwarden/web-vault
echo "<html><body><h1>Web Vault Placeholder</h1></body></html>" > target/lambda/vaultwarden/web-vault/index.html
- name: Archive function package
uses: actions/upload-artifact@v4
with:
name: vaultwarden-lambda
path: target/lambda/vaultwarden/*

7
CargoLambda.toml Normale Datei
Datei anzeigen

@ -0,0 +1,7 @@
[build]
features = ["aws"]
release = true
arm64 = true
[build.compiler]
type = "cargo"

3
aws/.gitignore gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,3 @@
.aws-sam
vaultwarden-lambda.zip
web-vault

53
aws/README.md Normale Datei
Datei anzeigen

@ -0,0 +1,53 @@
# AWS Serverless Deployment Instructions
## Architecture
```
CloudFront CDN
├─ API Lambda Function
│ ├─ Data S3 Bucket
│ ├─ Aurora DSQL Database
│ └─ Amazon Simple Email Service (SES)
└─ Web-vault static assets S3 Bucket
```
## A Note On AWS Accounts and Security
It is common to have one AWS account host multiple services. But it's easy, and doesn't cost any additional amount, to separate workloads into their own accounts. Doing so makes it easier to control for security concerns and monitor costs. AWS Identity and Access Management (IAM) enforces additional controls for cross-account access than for within-account access, for example, making it harder for security attacks to hop from workload to workload when they are in separate accounts.
Given the confidential nature of data stored in Vaultwarden, it is *highly* recommended that you create a new, separate AWS account just for Vaultwarden. If you only have one account, investigate creating an [AWS Organization](https://aws.amazon.com/organizations/) to make it easy to create a second account tied to the same billing and account management mechanism, and investigate creating an [AWS IAM Identity Center](https://aws.amazon.com/iam/identity-center/) instance for easy SSO access across your accounts.
## Initial Deployment
1. Create an AWS account
1. Install the AWS CLI
1. Install AWS SAM CLI
1. Download the vaultwarden-lambda.zip Lambda Function code package (e.g. from POC GHA artifact from run https://github.com/txase/vaultwarden/actions/runs/13315966383) to this directory
1. Pick a region that supports DSQL to deploy the Vaultwarden application into (must be one of us-east-1 or us-east-2 during DSQL Preview)
1. Create an Aurora DSQL Cluster in the region using the AWS Console (this will be automated when CloudFormation ships DSQL support at GA)
1. Setup local AWS configuration to access account and region from CLI
1. Copy DSQL Cluster ID
1. Run `./deploy.sh` in this directory
* Most parameters can be skipped at first, but you must provide the `DSQLClusterID` parameter value.
1. Note the "Output" values from the deploy command
* These can also be retrieved later by running `sam list stack-outputs`
1. Download the latest [web-vault build](https://github.com/dani-garcia/bw_web_builds/releases) and extract it
1. Sync the web-vault build contents into the WebVaultAssetsBucket:
* Inside the web-vault build folder run `aws s3 sync . s3://<WebVaultAssetsBucket>`, where `WebVaultAssetsBucket` is a stack output value
1. You can now navigate to your instance at the location of your `CDNDomain` stack output value
## Custom Domain
1. Create an AWS Certificate Manager (ACM) Certificate for your domain **in the us-east-1 region**
* There are many tutorials and/or automated ways to do this, including following the official docs [here](https://docs.aws.amazon.com/acm/latest/userguide/acm-public-certificates.html)
* It must be in the us-east-1 region because CloudFront only supports certificates from us-east-1
* Use key algorithm RSA 2048
* Continue to the next step once the certificate is in the *Issued* state
* Note the certificate's ARN
1. Run `./deploy.sh` again and add the following parameter values:
* **Domain**: `https://<custom domain>`
* **ACMCertificateArn**: The ARN of the certificate you created for the domain
1. Create a CNAME record for the custom domain set to the value of the CDNDomain stack output
## Email via AWS Simple Email Service (SES)
Email is complicated. These instructions will not attempt to walk you through setting up SES identities for sending email. You may find docs and guides online for how to do this.
In order for Vaultwarden to send emails using SES you must have an SES Email Address Identity that **does not have a default configuration set**. An identity with a default configuration set breaks the IAM permission model set up for the Vaultwarden API Function.
Once you have an SES Identity for the sending email address, run `./deploy.sh` again and provide the email address in the `SMTP_FROM` parameter.

9
aws/deploy.sh Ausführbare Datei
Datei anzeigen

@ -0,0 +1,9 @@
#!/bin/sh -e
echo 'Building template...'
sam build
echo ''
sam deploy --guided

12
aws/samconfig.toml Normale Datei
Datei anzeigen

@ -0,0 +1,12 @@
version = 0.1
[default.global.parameters]
stack_name = "vaultwarden"
[default.deploy.parameters]
resolve_s3 = true
s3_prefix = "vaultwarden"
confirm_changeset = true
capabilities = "CAPABILITY_IAM"
image_repositories = []
disable_rollback = true

582
aws/template.yaml Normale Datei
Datei anzeigen

@ -0,0 +1,582 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: AWS CloudFormation template for running VaultWarden on AWS serverless services.
Parameters:
Domain:
Type: String
Description: >-
The domain name for the Vaultwarden instance (e.g. https://example.com). If this parameter or the ACMCertificateArn
parameter are left empty, the Vaultwarden instance can still be reached at the output CDN domain
(e.g. https://xxxxxxxx.cloudfront.net).
AllowedPattern: (https://[a-z0-9.-]+|)
Default: ''
ACMCertificateArn:
Type: String
Description: The ARN of a us-east-1 ACM certificate to use for the domain. Required if the `Domain` parameter is set.
AllowedPattern: (arn:aws:acm:us-east-1:[0-9]+:certificate/[0-9a-f-]+|)
Default: ''
DSQLClusterId:
Type: String
Description: The endpoint of the DSQL database.
AllowedPattern: '[a-z0-9]+'
APILogRetention:
Type: Number
Description: The number of days to retain the API logs. -1 means to never expire.
Default: -1
AllowedValues: [-1, 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1096, 1827, 2192, 2557, 2922, 3288, 3653]
SignupsAllowed:
Type: String
Description: Controls if new users can register
Default: 'true'
AllowedValues: ['true', 'false']
IconService:
Type: String
Description: Allowed icon service sources.
Default: bitwarden
AdminToken:
Type: String
Description: Token for the admin interface, preferably an Argon2 PCH string. If empty, the admin interface will be disabled.
Default: ''
SMTPFrom:
Type: String
Description: The email address to send emails from. Email service is disabled if this value is empty.
Default: ''
SMTPFromName:
Type: String
Description: The name to send emails from.
Default: Vaultwarden
Mappings:
IconSource:
internal:
CSP: ''
bitwarden:
CSP: https://icons.bitwarden.net/
duckduckgo:
CSP: https://icons.duckduckgo.com/ip3/
google:
CSP: https://www.google.com/s2/favicons https://*.gstatic.com/favicon
Conditions:
IsDomainAndCertificateSet: !And
- !Not [!Equals [!Ref Domain, '']]
- !Not [!Equals [!Ref ACMCertificateArn, '']]
IsApiLogRetentionNeverExpire: !Equals
- !Ref APILogRetention
- -1
IconSourceIsPredefined: !Or
- !Equals [!Ref IconService, internal]
- !Equals [!Ref IconService, bitwarden]
- !Equals [!Ref IconService, duckduckgo]
- !Equals [!Ref IconService, google]
IsAdminTokenEmpty: !Equals
- !Ref AdminToken
- ''
IsEmailEnabled: !Not
- !Equals
- !Ref SMTPFrom
- ''
Resources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- BucketKeyEnabled: true
ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-${AWS::Region}-data
CorsConfiguration:
CorsRules:
- AllowedMethods:
- GET
- HEAD
AllowedOrigins:
- '*'
LifecycleConfiguration:
Rules:
- AbortIncompleteMultipartUpload:
DaysAfterInitiation: 2
ExpiredObjectDeleteMarker: true
NoncurrentVersionExpiration:
NoncurrentDays: 30
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
DataBucketEnforceEncryptionAndStorageTier:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref DataBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyUnencryptedObjectUploads
Effect: Deny
Principal: '*'
Action: s3:PutObject
Resource: !Sub arn:${AWS::Partition}:s3:::${DataBucket}/*
Condition:
'Null':
s3:x-amz-server-side-encryption-aws-kms-key-id: true
- Sid: DenyUnencryptedTransit
Effect: Deny
Principal: '*'
Action: s3:*
Resource:
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}/*
Condition:
Bool:
aws:SecureTransport: false
- Sid: DenyNonIntelligentTieringStorageClass
Effect: Deny
Principal: '*'
Action: s3:PutObject
Resource: !Sub arn:aws:s3:::${DataBucket}/*
Condition:
StringNotEquals:
s3:x-amz-storage-class: INTELLIGENT_TIERING
ApiFunctionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: lambda.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: AccessAWSServices
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:ListBucket
- s3:PutObject
- s3:DeleteObject
Resource:
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}/*
- Effect: Allow
Action: dsql:DbConnectAdmin
Resource: !Sub arn:${AWS::Partition}:dsql:${AWS::Region}:${AWS::AccountId}:cluster/${DSQLClusterId}
- !If
- IsEmailEnabled
- Effect: Allow
Action: ses:SendRawEmail
Resource: '*'
Condition:
StringEquals:
ses:FromAddress: !Ref SMTPFrom
ses:FromDisplayName: !Ref SMTPFromName
- !Ref AWS::NoValue
ApiFunction:
Type: AWS::Lambda::Function
Properties:
Architectures:
- arm64
Code: ./vaultwarden-lambda.zip
Environment:
Variables:
AWS_LWA_PORT: 8000
AWS_LWA_READINESS_CHECK_PATH: /alive
AWS_LWA_ASYNC_INIT: true
AWS_LWA_ENABLE_COMPRESSION: true
AWS_LWA_INVOKE_MODE: RESPONSE_STREAM
DATA_FOLDER: !Sub s3://${DataBucket}
TMP_FOLDER: /tmp
DATABASE_URL: !Sub dsql://${DSQLClusterId}.dsql.${AWS::Region}.on.aws
ENABLE_WEBSOCKET: false
DOMAIN: !If
- IsDomainAndCertificateSet
- !Ref Domain
- !Ref AWS::NoValue
SIGNUPS_ALLOWED: !Ref SignupsAllowed
IP_HEADER: X-Forwarded-For
ICON_SERVICE: !Ref IconService
ICON_REDIRECT_CODE: 301
ADMIN_TOKEN: !If
- IsAdminTokenEmpty
- !Ref AWS::NoValue
- !Ref AdminToken
SMTP_FROM: !If
- IsEmailEnabled
- !Ref SMTPFrom
- !Ref AWS::NoValue
SMTP_FROM_NAME: !Ref SMTPFromName
USE_AWS_SES: true
FunctionName: !Sub ${AWS::StackName}-api
Handler: bootstrap
Layers:
- !Sub arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerArm64:24
MemorySize: 3008 # Maximum value allowed for new accounts, higher value reduces cold start times, should still fit under free tier usage for personal use
Role: !GetAtt ApiFunctionRole.Arn
Runtime: provided.al2023
Timeout: 300
ApiFunctionLogs:
Type: AWS::Logs::LogGroup
DeletionPolicy: RetainExceptOnCreate
Properties:
LogGroupName: !Sub /aws/lambda/${ApiFunction}
RetentionInDays: !If
- IsApiLogRetentionNeverExpire
- !Ref AWS::NoValue
- !Ref APILogRetention
ApiFunctionUrl:
Type: AWS::Lambda::Url
Properties:
TargetFunctionArn: !Ref ApiFunction
AuthType: NONE
InvokeMode: RESPONSE_STREAM
ApiFunctionUrlPublicPermissions:
Type: AWS::Lambda::Permission
Properties:
Action: lambda:InvokeFunctionUrl
FunctionName: !Ref ApiFunction
Principal: '*'
FunctionUrlAuthType: NONE
WebVaultAssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-${AWS::Region}-web-vault
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
WebVaultAssetsBucketEnforceEncryptionInTransitAndStorageTier:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref DataBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyUnencryptedTransit
Effect: Deny
Principal: '*'
Action: s3:*
Resource:
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}
- !Sub arn:${AWS::Partition}:s3:::${DataBucket}/*
Condition:
Bool:
aws:SecureTransport: false
- Sid: DenyNonIntelligentTieringStorageClass
Effect: Deny
Principal: '*'
Action: s3:PutObject
Resource: !Sub arn:aws:s3:::${DataBucket}/*
Condition:
StringNotEquals:
s3:x-amz-storage-class: INTELLIGENT_TIERING
WebVaultAssetsBucketOriginAccessControl:
Type: AWS::CloudFront::OriginAccessControl
Properties:
OriginAccessControlConfig:
Name: !Sub ${AWS::StackName}-${AWS::Region}-web-vault-access-control
OriginAccessControlOriginType: s3
SigningBehavior: always
SigningProtocol: sigv4
# The following mirrors the header values in util.rs
ResponseHeaderPolicy:
Type: AWS::CloudFront::ResponseHeadersPolicy
Properties:
ResponseHeadersPolicyConfig:
Name: !Sub ${AWS::StackName}-${AWS::Region}
CustomHeadersConfig:
Items:
- Header: Cache-Control
Override: false
Value: no-cache, no-store, max-age=0
- Header: X-Robots-Tag
Override: true
Value: noindex, nofollow
- Header: Permissions-Policy
Override: true
Value: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()
SecurityHeadersConfig:
ContentSecurityPolicy:
ContentSecurityPolicy: !Sub
- >-
default-src 'self';
base-uri 'self';
form-action 'self';
object-src 'self' blob:;
script-src 'self' 'wasm-unsafe-eval';
style-src 'self' 'unsafe-inline';
child-src 'self' https://*.duosecurity.com https://*.duofederal.com;
frame-src 'self' https://*.duosecurity.com https://*.duofederal.com;
frame-ancestors 'self'
chrome-extension://nngceckbapebfimnlniiiahkandclblb
chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh
moz-extension://*;
img-src 'self' data:
https://haveibeenpwned.com
${IconServiceCSP};
connect-src 'self'
https://api.pwnedpasswords.com
https://api.2fa.directory
https://app.simplelogin.io/api/
https://app.addy.io/api/
https://api.fastmail.com/
https://api.forwardemail.net
https://${DataBucket.RegionalDomainName};
- IconServiceCSP: !If
- IconSourceIsPredefined
- !FindInMap [IconSource, !Ref IconService, CSP]
- !Select
- 0
- !Split ['{', !Ref IconService]
Override: true
ContentTypeOptions:
Override: true
FrameOptions:
FrameOption: SAMEORIGIN
Override: true
ReferrerPolicy:
Override: true
ReferrerPolicy: same-origin
StrictTransportSecurity:
AccessControlMaxAgeSec: 63072000
IncludeSubdomains: true
Override: true
Preload: true
XSSProtection:
Override: true
Protection: false
# The following mirrors the header values in util.rs
ConnectorHtmlResponseHeaderPolicy:
Type: AWS::CloudFront::ResponseHeadersPolicy
Properties:
ResponseHeadersPolicyConfig:
Name: !Sub ${AWS::StackName}-${AWS::Region}-connector-html
CustomHeadersConfig:
Items:
- Header: Cache-Control
Override: true
Value: no-cache, no-store, max-age=0
- Header: X-Robots-Tag
Override: true
Value: noindex, nofollow
- Header: Permissions-Policy
Override: true
Value: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), keyboard-map=(), magnetometer=(), microphone=(), midi=(), payment=(), picture-in-picture=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=()
SecurityHeadersConfig:
ContentTypeOptions:
Override: true
ReferrerPolicy:
Override: true
ReferrerPolicy: same-origin
StrictTransportSecurity:
AccessControlMaxAgeSec: 63072000
IncludeSubdomains: true
Override: true
Preload: true
XSSProtection:
Override: true
Protection: false
CDN:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases: !If
- IsDomainAndCertificateSet
- - !Select
- 2
- !Split
- /
- !Ref Domain
- !Ref AWS::NoValue
CacheBehaviors:
- AllowedMethods:
- DELETE
- HEAD
- GET
- OPTIONS
- PATCH
- POST
- PUT
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /api/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- AllowedMethods:
- DELETE
- HEAD
- GET
- OPTIONS
- PATCH
- POST
- PUT
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /admin
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- AllowedMethods:
- DELETE
- HEAD
- GET
- OPTIONS
- PATCH
- POST
- PUT
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /admin/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- AllowedMethods:
- DELETE
- HEAD
- GET
- OPTIONS
- PATCH
- POST
- PUT
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /events/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- AllowedMethods:
- DELETE
- HEAD
- GET
- OPTIONS
- PATCH
- POST
- PUT
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /identity/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /css/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /vw_static/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
Compress: true
OriginRequestPolicyId: b689b0a8-53d0-40ab-baf2-68738e2966ac # AllViewerExceptHostHeader
PathPattern: /icons/*
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: Api
ViewerProtocolPolicy: redirect-to-https
- CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
PathPattern: '*.html'
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: WebVaultAssetsBucket
ViewerProtocolPolicy: redirect-to-https
- CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled
Compress: true
PathPattern: '*connector.html'
ResponseHeadersPolicyId: !Ref ConnectorHtmlResponseHeaderPolicy
TargetOriginId: WebVaultAssetsBucket
ViewerProtocolPolicy: redirect-to-https
Comment: Vaultwarden CDN
CustomErrorResponses:
- ErrorCode: 403
ResponseCode: 200
ResponsePagePath: /404.html
DefaultCacheBehavior:
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized
Compress: true
ResponseHeadersPolicyId: !Ref ResponseHeaderPolicy
TargetOriginId: WebVaultAssetsBucket
ViewerProtocolPolicy: redirect-to-https
DefaultRootObject: index.html
Enabled: true
HttpVersion: http2and3
IPV6Enabled: true
Origins:
- Id: WebVaultAssetsBucket
DomainName: !GetAtt WebVaultAssetsBucket.RegionalDomainName
OriginAccessControlId: !GetAtt WebVaultAssetsBucketOriginAccessControl.Id
S3OriginConfig:
OriginAccessIdentity: ''
- Id: Api
CustomOriginConfig:
OriginProtocolPolicy: https-only
OriginSSLProtocols:
- TLSv1.2
DomainName: !Select
- 2
- !Split
- /
- !GetAtt ApiFunctionUrl.FunctionUrl
PriceClass: PriceClass_All
ViewerCertificate: !If
- IsDomainAndCertificateSet
- AcmCertificateArn: !Ref ACMCertificateArn
MinimumProtocolVersion: TLSv1.2_2021
SslSupportMethod: sni-only
- !Ref AWS::NoValue
WebVaultAssetsBucketPolicyCloudFrontAccess:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref WebVaultAssetsBucket
PolicyDocument:
Id: CloudFrontAccess
Version: '2012-10-17'
Statement:
- Principal:
Service: !Sub cloudfront.${AWS::URLSuffix}
Action: s3:GetObject
Effect: Allow
Resource: !Sub ${WebVaultAssetsBucket.Arn}/*
Condition:
StringEquals:
AWS:SourceArn: !Sub arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CDN.Id}
Outputs:
WebVaultAssetsBucket:
Value: !Ref WebVaultAssetsBucket
CDNDomain:
Value: !GetAtt CDN.DomainName