|
| 1 | +# Rust Infrastructure hosting for static websites |
| 2 | + |
| 3 | +The Rust Infrastructure team provides hosting for static websites available for |
| 4 | +all Rust teams. This document explains the requirements a website needs to meet |
| 5 | +and how to setup one. |
| 6 | + |
| 7 | +## Requirements for hosting websites |
| 8 | + |
| 9 | +* **The website must be managed by a Rust team, or be officially affiliated with |
| 10 | + the project.** |
| 11 | + The infrastructure team has finite resources and we can't offer hosting for |
| 12 | + community projects. |
| 13 | +* **The website’s content and build tooling must be hosted on a GitHub |
| 14 | + repository in either the [rust-lang](https://github.com/rust-lang) or |
| 15 | + [rust-lang-nursery](https://github.com/rust-lang-nursery) organizations.** |
| 16 | + The infrastructure team must be able to rebuild the website content at any |
| 17 | + time (for example if we need to switch hosting), and having it hosted on a |
| 18 | + GitHub repository inside infra-managed organizations is the best way for us |
| 19 | + to ensure that. Even though we'd prefer for all the repositories to be public |
| 20 | + it's not a requirement. |
| 21 | +* **The website must be built and deployed with a CI service.** |
| 22 | + We have custom tooling built around hosting static websites on our infra, and |
| 23 | + at the moment they work with Travis CI and Azure Pipelines. If you need |
| 24 | + different CI services ask us in advance and we'll adapt the tooling to your |
| 25 | + provider of choice. |
| 26 | +* **The website must reach an A+ grade on the |
| 27 | + [Mozilla Observatory](https://observatory.mozilla.org/).** |
| 28 | + Browsers have multiple security features toggleable only through HTTP |
| 29 | + response headers, and those features enhance users' privacy and prevent |
| 30 | + exploits from working. An A+ grade on the Observatory indicates all the |
| 31 | + important headers are correctly set. |
| 32 | +* **The website must be hosted on platforms vetted by the infra team.** |
| 33 | + We recommend either GitHub Pages or Amazon S3 (in the rust-lang AWS account) |
| 34 | + as the hosting and CloudFront as the CDN, but if you need other platforms |
| 35 | + that's good as long as we consider them secure and reliable. |
| 36 | + |
| 37 | +## Static websites configuration |
| 38 | + |
| 39 | +To avoid limitations of some hosting providers we have setup CloudFront to |
| 40 | +enable additional, custom behaviors. These behaviors are configured through a |
| 41 | +file named `website_config.json` at the root of the generated website content. |
| 42 | + |
| 43 | +### Adding custom headers |
| 44 | + |
| 45 | +One of the requirements for having a static website hosted by the |
| 46 | +infrastructure team is to reach an A+ grade on the [Mozilla |
| 47 | +Observatory](https://observatory.mozilla.org/), and that requires custom |
| 48 | +headers to be set. To setup custom headers you need to add an `headers` section |
| 49 | +to `website_config.json`. This example content includes all the headers |
| 50 | +needed to reach grade B on the Observatory (to reach grade A+ a Content |
| 51 | +Security Policy is required): |
| 52 | + |
| 53 | +```json |
| 54 | +{ |
| 55 | + "headers": { |
| 56 | + "Strict-Transport-Security": "max-age=63072000", |
| 57 | + "X-Content-Type-Options": "nosniff", |
| 58 | + "X-Frame-Options": "DENY", |
| 59 | + "X-XSS-Protection": "1; mode=block", |
| 60 | + "Referrer-Policy": "no-referrer, strict-origin-when-cross-origin" |
| 61 | + } |
| 62 | +} |
| 63 | +``` |
| 64 | + |
| 65 | +### Fixing GitHub Pages redirects |
| 66 | + |
| 67 | +GitHub Pages behaves weirdly when it sits behind CloudFront and it needs to |
| 68 | +issue redirects: since it doesn't know the real domain name it will use |
| 69 | +`http://org-name.github.io/repo-name` as the base of the redirect instead of |
| 70 | +the correct protocol and domain. To prevent this behavior the |
| 71 | +`github_pages_origin` key needs to be added to `website_config.json` |
| 72 | +with the origin base url as the value (excluding the protocol): |
| 73 | + |
| 74 | +```json |
| 75 | +{ |
| 76 | + "github_pages_origin": "org-name.github.io/repo-name" |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## Deployment guide |
| 81 | + |
| 82 | +These deployments steps are meant to be executed by a member of the |
| 83 | +infrastructure team since they require access to our AWS account. |
| 84 | + |
| 85 | +### Configuring AWS |
| 86 | + |
| 87 | +Create a CloudFront web distribution and set the following properties: |
| 88 | + |
| 89 | +- **Origin Domain Name:** rust-lang.github.io/repo-name |
| 90 | +- **Origin Protocol Policy:** HTTPS Only |
| 91 | +- **Viewer Protocol Policy:** Redirect HTTP to HTTPS |
| 92 | +- **Lambda Function Association:** |
| 93 | + - **Viewer Response:** arn:aws:lambda:us-east-1:890664054962:function:static-websites:4 |
| 94 | +- **Alternate Domain Names:** your-subdomain-name.rust-lang.org |
| 95 | +- **SSL Certificate:** Custom SSL Certificate |
| 96 | + - You will need to request the certificate for that subdomain name through |
| 97 | + ACM (please use the DNS challenge to validate the certificate) |
| 98 | +- **Comment:** your-subdomain-name.rust-lang.org |
| 99 | + |
| 100 | +Wait until the distribution is propagated and take note of its |
| 101 | +`.cloudfront.net` domain name. |
| 102 | + |
| 103 | +Head over to the domain’s Route 53 hosted zone and create a new record set: |
| 104 | + |
| 105 | +- **Name:** your-subdomain-name |
| 106 | +- **Type:** CNAME |
| 107 | +- **Value:** the `.cloudfront.net` domain name you saw earlier |
| 108 | + |
| 109 | +Create an AWS IAM user to allow the CI provider used to deploy website changes |
| 110 | +to perform whitelisted automatic actions. Use `ci--ORG-NAME--REPO-NAME` (for |
| 111 | +example `ci--rust-lang--rust`) as the user name, allow programmatic access to |
| 112 | +it and add it to the `ci-static-websites` IAM group. Then take note of the |
| 113 | +access key id and the secret access key since you’ll need those later. |
| 114 | + |
| 115 | +### Adding deploy keys |
| 116 | + |
| 117 | +To deploy websites we don’t use GitHub tokens (since they don’t have granular |
| 118 | +access scoping) but a deploy key with write access unique for each repository. |
| 119 | +To setup the deploy key you need to be an administrator on the repository, |
| 120 | +clone the [simpleinfra](https://github.com/rust-lang/simpleinfra) repository |
| 121 | +and run this command: |
| 122 | + |
| 123 | +``` |
| 124 | +$ cargo run --bin setup-deploy-keys rust-lang/repo-name |
| 125 | +``` |
| 126 | + |
| 127 | +The command requires the `GITHUB_TOKEN` ([you can generate one |
| 128 | +here](https://github.com/settings/tokens)) and the `TRAVIS_TOKEN` ([you can see |
| 129 | +yours here](https://travis-ci.com/account/preferences)) to be present. It will |
| 130 | +generate a brand new key, upload it to GitHub and configure Travis CI to use |
| 131 | +it if the repo is active there. |
| 132 | + |
| 133 | +### Configuring Travis CI |
| 134 | + |
| 135 | +To actually deploy the website, this snippet needs to be added to your |
| 136 | +`.travis.yml` (please replace the contents of `RUSTINFRA_DEPLOY_DIR` and |
| 137 | +`RUSTINFRA_CLOUDFRONT_DISTRIBUTION`): |
| 138 | + |
| 139 | +```yaml |
| 140 | +env: |
| 141 | + RUSTINFRA_DEPLOY_DIR: path/to/be/deployed |
| 142 | + RUSTINFRA_CLOUDFRONT_DISTRIBUTION: ABCDEFGHIJKLMN |
| 143 | +import: |
| 144 | + - rust-lang/simpleinfra/travis-configs/static-websites.yml |
| 145 | +``` |
| 146 | +
|
| 147 | +You will also need to set the contents of the `AWS_ACCESS_KEY_ID` and |
| 148 | +`AWS_SECRET_ACCESS_KEY` environment variables on the Travis CI web UI with the |
| 149 | +credentials of the IAM user you created earlier. The secret access key **must** |
| 150 | +be hidden from the build log, while the access key id should be publicly |
| 151 | +visible. |
| 152 | + |
| 153 | +### Configuring Azure Pipelines |
| 154 | + |
| 155 | +To actually deploy the website, this snippet needs to be added at the top of |
| 156 | +your pipeline's YAML file: |
| 157 | + |
| 158 | +```yaml |
| 159 | +resources: |
| 160 | + repositories: |
| 161 | + - repository: rustinfra |
| 162 | + type: github |
| 163 | + name: rust-lang/simpleinfra |
| 164 | + endpoint: rust-lang |
| 165 | +``` |
| 166 | + |
| 167 | +Then you can add this steps when you want to execute the deploy (please replace |
| 168 | +the contents of `deploy_dir` and `cloudfront_distribution`): |
| 169 | + |
| 170 | +```yaml |
| 171 | +- template: azure-configs/static-websites.yml@rustinfra |
| 172 | + parameters: |
| 173 | + deploy_dir: path/to/output |
| 174 | + # Optional, only needed if GitHub pages is behind CloudFront |
| 175 | + cloudfront_distribution: AAAAAAAAAAAAAA |
| 176 | +``` |
| 177 | + |
| 178 | +You will also need to set the following environment variables in the pipeline: |
| 179 | + |
| 180 | +* `GITHUB_DEPLOY_KEY`: value outputted when adding the deploy key earlier |
| 181 | + (**secret**) |
| 182 | +* `AWS_ACCESS_KEY_ID`: access key ID of the IAM user allowed to invalidate |
| 183 | + CloudFront (public) |
| 184 | +* `AWS_SECRET_ACCESS_KEY`: access key of the IAM user allowed to invalidate |
| 185 | + CloudFront (**secret**) |
0 commit comments