Skip to content

Commit f36311a

Browse files
authored
Merge pull request #511 from ampproject/master
Snapshot release version 6.
2 parents c54d365 + ed35f5e commit f36311a

27 files changed

+348
-92
lines changed

.github/workflows/prerequisites.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Check prerequisites
2+
on: [push, pull_request]
3+
jobs:
4+
check-format:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Setup Go 1.16
8+
uses: actions/setup-go@v1
9+
with:
10+
go-version: '1.16'
11+
12+
- name: Install goimports
13+
run: go get golang.org/x/tools/cmd/goimports
14+
15+
- name: Checkout the repository
16+
uses: actions/checkout@v2
17+
18+
# Comment this out for now until amppackager passes goimports.
19+
# https://github.com/ampproject/amppackager/issues/506
20+
# - name: Check formatting with goimports
21+
# run: |
22+
# if $(go env GOPATH)/bin/goimports -d . | grep . ; then
23+
# exit 1
24+
# fi
25+
26+
go-vet:
27+
runs-on: ubuntu-latest
28+
steps:
29+
- name: Setup Go 1.16
30+
uses: actions/setup-go@v1
31+
with:
32+
go-version: '1.16'
33+
34+
- name: Checkout the repository
35+
uses: actions/checkout@v2
36+
37+
- name: Diagnose the code with go vet
38+
# TODO(banaag): Turn on composite checking when
39+
# https://github.com/ampproject/amppackager/issues/507 is fixed.
40+
run: go vet -composites=false ./...
41+
42+
go-test:
43+
runs-on: ubuntu-latest
44+
steps:
45+
- name: Setup Go 1.16
46+
uses: actions/setup-go@v1
47+
with:
48+
go-version: '1.16'
49+
50+
- name: Checkout the repository
51+
uses: actions/checkout@v2
52+
53+
- name: Run the tests
54+
run: go test ./...

.travis.yml

Lines changed: 0 additions & 15 deletions
This file was deleted.

README.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,21 @@ own and can obtain certificates for.
3737
1. Get amppackager.
3838

3939
Check your Go version by running `go version`.
40-
41-
For Go 1.14 and higher versions run:
40+
41+
For Go 1.16 and higher run:
42+
43+
```
44+
git clone https://github.com/ampproject/amppackager.git my-amp-directory
45+
cd my-amp-directory
46+
go install github.com/ampproject/amppackager/cmd/amppkg
47+
```
48+
49+
For Go 1.14 and Go 1.15 run:
4250

4351
```
4452
go get -u github.com/ampproject/amppackager/cmd/amppkg
4553
```
46-
54+
4755
For Go 1.13 and earlier versions run:
4856

4957
```

amppkg.example.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,6 @@ ForwardedRequestHeaders = []
191191
# PathRE = "/world/.*"
192192
# QueryRE = ""
193193

194-
# IMPORTANT NOTE: the support of the ACME protocol and automatic renewal of certificates is currently in the
195-
# EXPERIMENTAL stage. Once we have more experience with people using it out in the wild, we will gradually
196-
# move it to PRODUCTION mode.
197-
#
198194
# ACME is a protocol that allows for automatic renewal of certificates. AMP Packager uses an ACME library
199195
# https://github.com/go-acme/lego to handle certificate renewal. Automatic certificate renewal is enabled
200196
# in AMP Packager via the 'autorenewcert' flag. Turning the flag on will enable AMP Packager to automatically
@@ -227,6 +223,15 @@ ForwardedRequestHeaders = []
227223
# request signed exchange certificates.
228224
# EmailAddress = "user@company.com"
229225

226+
# The EABKid and EABHmac need to have synchronized values. They can both be empty (in which case EAB is not used)
227+
# or both have valid values. If one is empty and the other is not, the AMP Packager will generate an error.
228+
# This is the Key Identifier from ACME CA. Used for External Account Binding.
229+
# EABKid = "eab.kid"
230+
231+
# This is the MAC Key from ACME CA. Used for External Account Binding. Should be in
232+
# Base64 URL Encoding without padding format.
233+
# EABHmac = "eab.hmac"
234+
230235
# For the remaining configuration items, it's important to understand the different challenges employed as
231236
# part of the ACME protocol. See:
232237
# https://ietf-wg-acme.github.io/acme/draft-ietf-acme-acme.html#identifier-validation-challenges
@@ -265,6 +270,8 @@ ForwardedRequestHeaders = []
265270
# [ACMEConfig.Development]
266271
# DiscoURL = "https://development-acme.discovery.url/"
267272
# EmailAddress = "user@company.com"
273+
# EABKid = "eab.kid"
274+
# EABHmac = "eab.hmac"
268275
# HttpChallengePort = 5002
269276
# HttpWebRootDir = '/path/to/www_root_dir'
270277
# TlsChallengePort = 5003

deploy/gcloud/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,61 @@ The following information can be customized in setup.sh, but the default also wo
118118

119119
kubectl apply -f amppackager_service.yaml
120120

121+
## Production Reverse Proxy Setup
122+
123+
For now, productionizing is a bit manual. The minimum steps are:
124+
125+
1. Don't expose amppkg to the outside world. If possible, keep it on your internal network.
126+
In addition, use the loadBalancerSourceRanges above so that it only responds to the reverse
127+
proxy you set up. More information could be found here:
128+
1. [Kubernetes Firewall Configuration](https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/)
129+
2. [Subnet Calculator](https://mxtoolbox.com/subnetcalculator.aspx)
130+
3. [Understanding CIDR notation](https://www.digitalocean.com/community/tutorials/understanding-ip-addresses-subnets-and-cidr-notation-for-networking)
131+
2. Configure your TLS-serving frontend server to conditionally proxy to
132+
`amppkg`:
133+
1. If the URL starts with `/amppkg/`, forward the request unmodified.
134+
2. If the URL points to an AMP page and the `AMP-Cache-Transform` request
135+
header is present, rewrite the URL by prepending `/priv/doc` and forward
136+
the request.
137+
138+
NOTE: If using nginx, prefer using `proxy_pass` with `$request_uri`,
139+
rather than using `rewrite`, as in [this PR](https://github.com/Warashi/try-amppackager/pull/3),
140+
to avoid percent-encoding issues.
141+
3. If at all possible, don't send URLs of non-AMP pages to `amppkg`; its
142+
[transforms](transformer/) may break non-AMP HTML.
143+
4. DO NOT forward `/priv/doc` requests; these URLs are meant to be
144+
generated by the frontend server only.
145+
3. For HTTP compliance, ensure the `Vary` header set to `AMP-Cache-Transform,
146+
Accept` for all URLs that point to an AMP page, irrespective of whether the
147+
response is HTML or SXG. (SXG responses that come from `amppkg` will have
148+
the appropriate `Vary` header set, so it may only be necessary to
149+
explicitly set the `Vary` header for HTML responses.)
150+
4. Get an SXG cert from your CA. It must use an EC key with the prime256v1
151+
algorithm, and it must have a [CanSignHttpExchanges
152+
extension](https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html#cross-origin-cert-req).
153+
One provider of SXG certs is [DigiCert](https://www.digicert.com/account/ietf/http-signed-exchange.php).
154+
You can fill in your ACME Directory URL using these [steps](https://docs.digicert.com/certificate-tools/Certificate-lifecycle-automation-index/acme-user-guide/acme-directory-urls-signed-http-exchange-certificates/).
155+
5. Every 90 days or sooner, renew your SXG cert (per
156+
[WICG/webpackage#383](https://github.com/WICG/webpackage/pull/383)) and
157+
restart amppkg (per
158+
[#93](https://github.com/ampproject/amppackager/issues/93)). This is only necessary if ACME
159+
cert auto-renewal is not enabled.
160+
6. Keep amppkg updated from either `releases` (the default branch, so `go get` works)
161+
about every ~2 months. The [wg-caching](https://github.com/ampproject/wg-caching)
162+
team will release a new version approximately this often. Soon after each
163+
release, Googlebot will increment the version it requests with
164+
`AMP-Cache-Transform`. Googlebot will only allow the latest 2-3 versions
165+
(details are still TBD), so an update is necessary but not immediately. If
166+
amppkg doesn't support the requested version range, it will fall back to
167+
serving unsigned AMP.
168+
169+
The version of amppkg on the [Google Cloud Marketplace](https://cloud.google.com/marketplace/)
170+
will also be updated once a new release is published.
171+
172+
To keep subscribed to releases, you can select "Releases only" from the
173+
"Watch" dropdown in GitHub, or use [various tools](https://stackoverflow.com/questions/9845655/how-do-i-get-notifications-for-commits-to-a-repository)
174+
to subscribe to the `releases` branch.
175+
121176
## Optionally, run the teardown script (gcloud_down.sh)
122177

123178
1. Go to the directory where you installed amppackager.

docs/cache_requirements.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ The Google AMP cache sets some requirements in addition to the ones set by the
1515
These include:
1616

1717
* The signed `fallback URL` must equal the URL at which the SXG was delivered.
18+
* The signed `cert-url` must be `https`.
1819
* The signature header must contain only:
1920
* One parameterised identifier.
2021
* Parameter values of type string, binary, or identifier.
@@ -34,7 +35,7 @@ These include:
3435
* The signed `content-security-policy` header must be present and comply with
3536
these rules:
3637
* `default-src`, `script-src`, `object-src`, `style-src`, and `report-uri`
37-
must equal those from the [AMP cache CSP](https://github.com/ampproject/amppackager/blob/releases/packager/signer/signer.go#L272)
38+
must equal those from the [AMP cache CSP](https://github.com/ampproject/amppackager/blob/c54d36556d56d5a604eea079ef7dc8067f67e1ea/packager/signer/signer.go#L244-L255)
3839
* `base-uri`, `block-all-mixed-content`, `font-src`, `form-action`,
3940
`manifest-src`, `referrer`, and `upgrade-insecure-requests` may be omitted
4041
or have any value

go.sum

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,15 +251,12 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
251251
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
252252
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
253253
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
254-
github.com/prometheus/client_golang v1.6.0 h1:YVPodQOcK15POxhgARIvnDRVpLcuK8mglnMrWfyrw6A=
255-
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
256254
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
257255
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
258256
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
259257
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
260258
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
261259
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
262-
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
263260
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
264261
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
265262
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=

packager/certcache/certcache.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,7 @@ func (this *CertCache) setCerts(certs []*x509.Certificate) {
717717
this.certs = certs
718718
this.certName = util.CertName(certs[0])
719719

720+
log.Printf("Writing cert %s to file %v", this.certName, this.CertFile)
720721
err := certloader.WriteCertsToFile(this.certs, this.CertFile)
721722
if err != nil {
722723
log.Printf("Unable to write certs to file: %s", this.CertFile)

packager/certfetcher/certfetcher.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
6060
// fetcher := CertFetcher()
6161
// fetcher.setUser(email, privateKey)
6262
// fetcher.bindToPort(port)
63-
func New(email string, certSignRequest *x509.CertificateRequest, privateKey crypto.PrivateKey,
64-
acmeDiscoURL string, httpChallengePort int, httpChallengeWebRoot string,
63+
func New(email string, eabKid string, eabHmac string, certSignRequest *x509.CertificateRequest,
64+
privateKey crypto.PrivateKey, acmeDiscoURL string, httpChallengePort int, httpChallengeWebRoot string,
6565
tlsChallengePort int, dnsProvider string, shouldRegister bool) (*CertFetcher, error) {
6666

6767
acmeUser := AcmeUser{
@@ -119,17 +119,32 @@ func New(email string, certSignRequest *x509.CertificateRequest, privateKey cryp
119119
}
120120
}
121121

122-
// Theoretically, this should always be set to false as users should have pre-registered for access
123-
// to the ACME CA and agreed to the TOS.
124-
// TODO(banaag): revisit this when trying the class out with Digicert CA.
122+
var reg *registration.Resource
125123
if !shouldRegister {
126124
acmeUser.Registration = new(registration.Resource)
125+
} else if reg, err = client.Registration.ResolveAccountByKey(); err == nil {
126+
// Check if we already have an account.
127+
acmeUser.Registration = reg
127128
} else {
129+
// We need to reset the LEGO client after calling Registration.ResolveAccountByKey().
130+
client, err = lego.NewClient(config)
131+
if err != nil {
132+
return nil, errors.Wrap(err, "Obtaining LEGO client.")
133+
}
128134
// TODO(banaag) make sure we present the TOS URL to the user and prompt for confirmation.
129135
// The plan is to move this to some separate setup command outside the server which would be
130136
// executed one time. Alternatively, we can have a field in the toml file that is documented
131137
// to indicate agreement with TOS.
132-
reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
138+
if eabKid == "" && eabHmac == "" {
139+
reg, err = client.Registration.Register(registration.RegisterOptions{
140+
TermsOfServiceAgreed: true})
141+
} else {
142+
reg, err = client.Registration.RegisterWithExternalAccountBinding(registration.RegisterEABOptions{
143+
TermsOfServiceAgreed: true,
144+
Kid: eabKid,
145+
HmacEncoded: eabHmac})
146+
}
147+
133148
if err != nil {
134149
return nil, errors.Wrap(err, "ACME CA client registration")
135150
}

packager/certfetcher/certfetcher_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ func TestNewFetcher(t *testing.T) {
8989
DNSNames: []string{"test.example.com"},
9090
}
9191

92-
fetcher, err := New("test@test.com", &csr, privateKey, apiURL+"/dir",
93-
5002, "", 0, "", false)
92+
fetcher, err := New("test@test.com", "eab.kid", "eab.hmac", &csr,
93+
privateKey, apiURL+"/dir", 5002, "", 0, "", false)
9494
assert.Nil(t, err)
9595
assert.NotNil(t, fetcher.legoClient)
9696
assert.Equal(t, "test@test.com", fetcher.AcmeUser.Email)
@@ -120,8 +120,8 @@ func TestFetchCertSuccess(t *testing.T) {
120120
DNSNames: []string{"test.example.com"},
121121
}
122122

123-
fetcher, err := New("test@test.com", &csr, privateKey, apiURL+"/dir",
124-
5002, "", 0, "", false)
123+
fetcher, err := New("test@test.com", "eab.kid", "eab.hmac", &csr,
124+
privateKey, apiURL+"/dir", 5002, "", 0, "", false)
125125
assert.Nil(t, err)
126126
assert.NotNil(t, fetcher)
127127

@@ -151,8 +151,8 @@ func TestFetchCertFail(t *testing.T) {
151151
DNSNames: []string{"test.example.com"},
152152
}
153153

154-
fetcher, err := New("test@test.com", &csr, privateKey, apiURL+"/dir",
155-
5002, "", 0, "", false)
154+
fetcher, err := New("test@test.com", "eab.kid", "eab.hmac", &csr,
155+
privateKey, apiURL+"/dir", 5002, "", 0, "", false)
156156
assert.Nil(t, err)
157157
assert.NotNil(t, fetcher)
158158

packager/certloader/certloader.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,22 @@ func CreateCertFetcher(config *util.Config, key crypto.PrivateKey, domain string
6464
return nil, errors.New("missing acme disco url")
6565
}
6666
acmeDiscoveryURL := acmeConfig.DiscoURL
67+
// Fields for External Account Binding. Some CAs require them, others like
68+
// DigiCert, do not. Either both eabKid and eabHmac has to be specified or
69+
// none of them are specified.
70+
if acmeConfig.EABKid == "" && acmeConfig.EABHmac != "" {
71+
return nil, errors.New("EABKid is empty, but EABHmac is not empty, both values need to be set or empty")
72+
}
73+
if acmeConfig.EABKid != "" && acmeConfig.EABHmac == "" {
74+
return nil, errors.New("EABKid is not empty, but EABHmac is empty, both values need to be set or empty")
75+
}
76+
eabKid := acmeConfig.EABKid
77+
eabHmac := acmeConfig.EABHmac
6778
if acmeConfig.HttpChallengePort == 0 &&
6879
acmeConfig.HttpWebRootDir == "" &&
6980
acmeConfig.TlsChallengePort == 0 &&
7081
acmeConfig.DnsProvider == "" {
71-
return nil, errors.New("One of HttpChallengePort, HttpWebRootDir, TlsChallengePort and DnsProvider must be present.")
82+
return nil, errors.New("one of HttpChallengePort, HttpWebRootDir, TlsChallengePort and DnsProvider must be present")
7283
}
7384
httpChallengePort := acmeConfig.HttpChallengePort
7485
httpWebRootDir := acmeConfig.HttpWebRootDir
@@ -83,8 +94,9 @@ func CreateCertFetcher(config *util.Config, key crypto.PrivateKey, domain string
8394
}
8495

8596
// Create the cert fetcher that will auto-renew the cert.
86-
certFetcher, err := certfetcher.New(emailAddress, csr, key, acmeDiscoveryURL,
87-
httpChallengePort, httpWebRootDir, tlsChallengePort, dnsProvider, true)
97+
certFetcher, err := certfetcher.New(emailAddress, eabKid, eabHmac, csr, key,
98+
acmeDiscoveryURL, httpChallengePort, httpWebRootDir, tlsChallengePort,
99+
dnsProvider, true)
88100
if err != nil {
89101
return nil, errors.Wrap(err, "creating certfetcher")
90102
}

packager/signer/signer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,12 +240,15 @@ func MutateFetchedContentSecurityPolicy(fetched string) string {
240240
}
241241
}
242242
// Add missing directives or replace the ones that were removed in some cases
243+
// NOTE: After changing this string, please update the permalink in
244+
// docs/cache_requirements.md.
243245
newCsp.WriteString(
244246
"default-src * blob: data:;" +
245247
"report-uri https://csp.withgoogle.com/csp/amp;" +
246248
"script-src blob: https://cdn.ampproject.org/rtv/ " +
247249
"https://cdn.ampproject.org/v0.js " +
248250
"https://cdn.ampproject.org/v0/ " +
251+
"https://cdn.ampproject.org/lts/ " +
249252
"https://cdn.ampproject.org/viewer/;" +
250253
"style-src 'unsafe-inline' https://cdn.materialdesignicons.com " +
251254
"https://cloud.typography.com https://fast.fonts.net " +

packager/signer/signer_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ func (this *SignerSuite) TestMutatesCspHeaders() {
468468
"block-all-mixed-content;"+
469469
"default-src * blob: data:;"+
470470
"report-uri https://csp.withgoogle.com/csp/amp;"+
471-
"script-src blob: https://cdn.ampproject.org/rtv/ https://cdn.ampproject.org/v0.js https://cdn.ampproject.org/v0/ https://cdn.ampproject.org/viewer/;"+
471+
"script-src blob: https://cdn.ampproject.org/rtv/ https://cdn.ampproject.org/v0.js https://cdn.ampproject.org/v0/ https://cdn.ampproject.org/lts/ https://cdn.ampproject.org/viewer/;"+
472472
"style-src 'unsafe-inline' https://cdn.materialdesignicons.com https://cloud.typography.com https://fast.fonts.net https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com https://p.typekit.net https://pro.fontawesome.com https://use.fontawesome.com https://use.typekit.net;"+
473473
"object-src 'none'",
474474
exchange.ResponseHeaders.Get("Content-Security-Policy"))

packager/util/config.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,17 @@ type ACMEConfig struct {
6666
}
6767

6868
type ACMEServerConfig struct {
69-
DiscoURL string // ACME Directory Resource URL
70-
AccountURL string // ACME Account URL. If non-empty, we
71-
// will auto-renew cert via ACME.
72-
EmailAddress string // Email address registered with ACME CA.
69+
// ACME Directory Resource URL
70+
AccountURL string
71+
// ACME Account URL. If non-empty, we will auto-renew cert via ACME.
72+
DiscoURL string
73+
// Email address registered with ACME CA.
74+
EmailAddress string
75+
// Key Identifier from ACME CA. Used for External Account Binding.
76+
EABKid string
77+
// MAC Key from ACME CA. Used for External Account Binding. Should be in
78+
// Base64 URL Encoding without padding format.
79+
EABHmac string
7380

7481
// See: https://letsencrypt.org/docs/challenge-types/
7582
// For non-wildcard domains, only one of HttpChallengePort, HttpWebRootDir or

0 commit comments

Comments
 (0)