diff --git a/Dockerfile b/Dockerfile index 56a84422f..b09eaab45 100644 --- a/Dockerfile +++ b/Dockerfile @@ -69,7 +69,8 @@ EXPOSE 2112 # Copy compiled appliation from the builder. ADD . /app/src/autograph -ADD autograph.yaml /app +ADD autograph-service.yaml /app +ADD autograph-signer.yaml /app ADD version.json /app COPY --from=builder /go/bin /go/bin/ @@ -81,4 +82,3 @@ RUN useradd --uid 10001 --home-dir /app --shell /sbin/nologin app USER app WORKDIR /app CMD ["/go/bin/autograph"] - diff --git a/README.md b/README.md index 23beb6520..912111bdd 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ $ go get github.com/mozilla-services/autograph The resulting binary will be placed in `$GOPATH/bin/autograph`. To run autograph with the example conf, do: ```bash $ cd $GOPATH/src/github.com/mozilla-services/autograph -$ $GOPATH/bin/autograph -c autograph.yaml +$ $GOPATH/bin/autograph -c autograph-service.yaml -s autograph-signer.yaml ``` Example clients are in the `tools` directory. You can install the Go one like this: diff --git a/authorize_test.go b/authorize_test.go index 72f0f1b84..583587230 100644 --- a/authorize_test.go +++ b/authorize_test.go @@ -17,7 +17,7 @@ import ( ) func TestMissingAuthorization(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") bodyrdr := bytes.NewReader(body) @@ -35,7 +35,7 @@ func TestMissingAuthorization(t *testing.T) { } func TestBogusAuthorization(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") bodyrdr := bytes.NewReader(body) @@ -54,7 +54,7 @@ func TestBogusAuthorization(t *testing.T) { } func TestBadPayload(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") bodyrdr := bytes.NewReader(body) @@ -62,7 +62,7 @@ func TestBadPayload(t *testing.T) { if err != nil { t.Fatal(err) } - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -78,7 +78,7 @@ func TestBadPayload(t *testing.T) { } func TestExpiredAuth(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") bodyrdr := bytes.NewReader(body) @@ -98,7 +98,7 @@ func TestExpiredAuth(t *testing.T) { } func TestDuplicateNonce(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) body := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaa") bodyrdr := bytes.NewReader(body) @@ -107,7 +107,7 @@ func TestDuplicateNonce(t *testing.T) { t.Fatal(err) } req.Header.Set("Content-Type", "application/json") - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -127,14 +127,14 @@ func TestDuplicateNonce(t *testing.T) { } func TestNonceFromLRU(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) req, err := http.NewRequest("POST", "http://foo.bar/sign/data", nil) if err != nil { t.Fatal(err) } - authCreds, err := ag.getAuthByID(conf.Authorizations[0].ID) + authCreds, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestNonceFromLRU(t *testing.T) { } func TestSignerNotFound(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) _, err := ag.authBackend.getSignerForUser(`unknown018qoegdxc`, `unkown093ytid`) if err == nil { @@ -183,7 +183,7 @@ func TestSignerNotFound(t *testing.T) { } func TestDefaultSignerNotFound(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) _, err := ag.authBackend.getSignerForUser(`unknown018qoegdxc`, ``) if err == nil { @@ -192,7 +192,7 @@ func TestDefaultSignerNotFound(t *testing.T) { } func TestAutographerAddAuthorizationsFails(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) testcases := []struct { name string @@ -292,7 +292,7 @@ func TestAutographerAddAuthorizationsFails(t *testing.T) { // set an authorization with a ts validity of 2 seconds, then sleep 5 seconds // to trigger the hawk skew error func TestHawkTimestampSkewFail(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) var err error ag.hawkMaxTimestampSkew, err = time.ParseDuration("2s") diff --git a/autograph-service.yaml b/autograph-service.yaml new file mode 100644 index 000000000..37a7d6ee5 --- /dev/null +++ b/autograph-service.yaml @@ -0,0 +1,46 @@ +server: + listen: "0.0.0.0:8000" + # cache 500k nonces to protect from authorization replay attacks + noncecachesize: 524288 + idletimeout: 60s + readtimeout: 60s + writetimeout: 60s + +debugserver: + listen: "0.0.0.0:2112" + +# When using an HSM, place the path to the pkcs11 client library, +# token label and pin of the crypto user in the configuration below +# hsm: +# # sample config for cloudhsm +# path: /opt/cloudhsm/lib/libcloudhsm_pkcs11.so +# tokenlabel: cavium +# pin: ulfr:e2deea623796eecd + +# When using a postgres database, set the config below: +# database: +# name: autograph +# user: myautographdbuser +# password: myautographdbpassword +# host: db:5432 +# sslmode: verify-full +# sslrootcert: /etc/ssl/certs/db-root.crt +# maxopenconns: 100 +# maxidleconns: 10 +# monitorpollinterval: 10s + +# When the DB or HSM is enabled use these timeouts for the +# /__heartbeat__ handler +heartbeat: + hsmchecktimeout: 100ms + dbchecktimeout: 150ms + +# allow hawk authorization headers to be valid for up to 10 minutes +# to account for diverging clocks between client and server +hawktimestampvalidity: 10m + +# optional refresh rate for cached monitor data +monitorinterval: 5m + +monitoring: + key: 19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs diff --git a/autograph.yaml b/autograph-signer.yaml similarity index 98% rename from autograph.yaml rename to autograph-signer.yaml index 3c5a46035..124bb1b16 100644 --- a/autograph.yaml +++ b/autograph-signer.yaml @@ -1,47 +1,3 @@ -server: - listen: "0.0.0.0:8000" - # cache 500k nonces to protect from authorization replay attacks - noncecachesize: 524288 - idletimeout: 60s - readtimeout: 60s - writetimeout: 60s - -debugserver: - listen: "0.0.0.0:2112" - -# When using an HSM, place the path to the pkcs11 client library, -# token label and pin of the crypto user in the configuration below -# hsm: -# # sample config for cloudhsm -# path: /opt/cloudhsm/lib/libcloudhsm_pkcs11.so -# tokenlabel: cavium -# pin: ulfr:e2deea623796eecd - -# When using a postgres database, set the config below: -# database: -# name: autograph -# user: myautographdbuser -# password: myautographdbpassword -# host: db:5432 -# sslmode: verify-full -# sslrootcert: /etc/ssl/certs/db-root.crt -# maxopenconns: 100 -# maxidleconns: 10 -# monitorpollinterval: 10s - -# When the DB or HSM is enabled use these timeouts for the -# /__heartbeat__ handler -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - -# allow hawk authorization headers to be valid for up to 10 minutes -# to account for diverging clocks between client and server -hawktimestampvalidity: 10m - -# optional refresh rate for cached monitor data -monitorinterval: 5m - # The keys below are testing keys that do not grant any power signers: # a p384 key, the standard @@ -1793,6 +1749,3 @@ authorizations: key: 3isey64n25fim18chqgewirm6z2gwva1mas0eu71e9jtisdwv6bd signers: - remote-settings - -monitoring: - key: 19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs diff --git a/autograph.encrypted.yaml b/autograph.encrypted.yaml deleted file mode 100644 index 815e1b44b..000000000 --- a/autograph.encrypted.yaml +++ /dev/null @@ -1,78 +0,0 @@ -server: - listen: ENC[AES256_GCM,data:lcmMdwCiL/h8tO0r,iv:KFa4V51wP52Dm09lcvQpeA3f00oipc8PUgxckh9n+iA=,tag:j2hgEMRMNBOwn3r9bJHOOg==,type:str] - # cache 500k nonces to protect from authorization replay attacks - noncecachesize: ENC[AES256_GCM,data:i2Uay2NP,iv:JV9kj4C5tAicl/t5VfLoWb0rCDoYjQgFTlNvRmbKR2Q=,tag:8zSqr/OLEVq9tOivvVS6jA==,type:int] - # The keys below are testing keys that do not grant any power - # a p384 key, the standard -signers: -- id: ENC[AES256_GCM,data:5nuDxQfd+g==,iv:yIYtGLKUAY1ktlhZMfQmQ+2I3A0lQ6K5LO6SUOlgS7U=,tag:FMmWKZU7LFrn8WQ0fx2mNw==,type:str] - type: ENC[AES256_GCM,data:y7QWk6gtyOkdaSYNo/mw8g==,iv:P+C04kg/FghWJzNnDWEgSXqxQjNXGpBkQXtKyZFPk2k=,tag:tYd126cmrp+9pRWEhsHN8g==,type:str] - privatekey: ENC[AES256_GCM,data:LiZkVou8uJSeO2RN0eiqeK6OfuiY8ZrJJbbUdeuNasRnwuGaGVgkbsVuoSxUFk+xUVP9zVTMIWdb1y/Q+B74zlV/1c8BZ9jJez22q99iK5W0EJM8gSMrg/G51P2nBznNuvGU2NF7Zc/83iY7Jn5kPG4kKzDUnbvqdbqtIrI7UOQotz8EgVLxJbO81Vvzk0PxqVYvdelU5XSK983DnXJ5evXHk3IMPc1WduJcLI+Ne//AG0VyhH7RzlkJs8oy2m+drr3V4t+I9wTnpv2EEhEJJB4O4BJfXOPeYCECBGA9CHSJMjqf1uq6WeejSk0VCuPFQ0K+wiL5g30+IgL4AoXW/JhYLujUPTQ6jMsp9Zpgfj/MpAv7veEfFdoEYum9XYFP3ox3GNg+ZRrd5xdJOXE1rL7iYKHQleaKhe8fa0jsDBzbl31R0juNguG2r+4mlSHOgK7wT6xEeTcZQIwhxkUewVyTXMaLzs0=,iv:h+laLZ0fvBEUbMesp9eMdPnBy9b9V7d/Mamgt5zqNSw=,tag:7PKw0mc/MthJx4sgT+Ygpw==,type:str] - # a p256 key -- id: ENC[AES256_GCM,data:ELqEYKDyOg==,iv:DAmOyCW9ESD2n5LZZL+5Ah6G+7qEqaPhCzLItk/oBvk=,tag:mws6ppJUC3nxrQ7fkcO+Jw==,type:str] - type: ENC[AES256_GCM,data:3UqmuElePDFA/nNqFoL+mw==,iv:V2jb7CGEOMy0rVNzgGOaDjv+QwxMI7hJ+3gBA4G/k6M=,tag:LMoUzrUPIUrwM3ecsCiQjA==,type:str] - privatekey: ENC[AES256_GCM,data:+0WFrqcUCC/YhsGFmptiRWvm4lh7ai902mmmgewUoqfeXOf3eQEIz56XK1g7RsYb1RNAnXTMkc+G66Ot2GIUmD/yLqSTWJFVt5FFcY41ARG6x89jqt9rHzuf/KUA1ActTNHwSShqjcXYwDWIhtCUOi00z9ES6PqzfXizoaxP0yghNQKCtCDqfTJVxqyhZmw/LPbF2i4df1BgC/U7Ir56AOqGayebImWVNHse9EDt//6Qy26VYvJdNiicdCmWF+dgpN1G5hMsTna3WvS7vbXWGEqziomfXdaGF1fd0e7AWQdAjgzqwTfBzHTgEZIBuWpesyZp08o009eomQOjj2B1DQ7i747YAfKNMQLmjK2P6WwtrtamDmy/BdpbmBq/PaDIsLi87kW0NMsoaSc8l7k=,iv:BHRSFeUtL++X2YN6KKvPJXQD5PS7KiKHjU4ZdaD7+zs=,tag:vzYMkFW+EzRCqAdkx/gi2Q==,type:str] - # p521, for testing, don't use p521 -- id: ENC[AES256_GCM,data:u3D2CQ0ZOQ==,iv:0TKNO8A9qijqA6TEREBrnq+ru938aJFe4WaGnQ9B7Es=,tag:kZWouBLN0MNC5tKuSY0/ig==,type:str] - type: ENC[AES256_GCM,data:aHfwvtKGLZCxMMWC1yM8qA==,iv:o9FKwXcME2kT0aaSegk+hI9luSHTPu2bAS9ou7Ik4Zc=,tag:7pcWfM2kxGTkhWRFtuFQZQ==,type:str] - privatekey: ENC[AES256_GCM,data:5152tm8zQ8H8ds5OhX83K0F70fRdtopkXguBI4M3nM3qRtG5L6Iau3fg881ntEbLi8kYrMeFEslqnVbnDgDPykVPjTQqGeqENU1HOv5IiOrsV3iFpMHN4GLqNbsdZ1juKEtcFpWy8aZG6pDHKdW/z0G1/kmBHVJDVJqLYswvc5S8sO0h1fbjIKJu2LMg9jeWp4fUkfDs59VPytu1YY13UaooTEkUwm6Ms9MZ7e0DoEp82grKebZ/73g1Jj1TPiPJXCn3ENd8cXOLQNQycjbxVL/O2SvABcnQiFnckaG+vHTDLOFLCLrXKgdOsJGKJ+ha91POTBwBiTnc9xj7kQOkWnj9m+tHZ5Hc7GMo2Cr3+n0ipH8PJ7WWP4Q0F2FnUAj7TwjdPDt+whQc1P3hyUrRoBBxVsjFRx2BuwodFWPKYNOLdx51umGXTpo74oO/i/Ymc3PWeLNpiovUUYIz1fkSzuLOQqLQKfKSbZVvY6gDfM6AT4X/LEKlmkfGvz2PW03B/9uJMEKP5JyTyeT3iMwgixwcKSACCneZek9Pqwhem/BP0/0hdCgHpBY0QlgDZhLRN6enlA==,iv:oKmDFRsD38T6181SDzLFTcenvaS9lReTlnukX3458Z0=,tag:mubJERxWXzrNkgv/CpWMZA==,type:str] - # Normandy dev chain with x5u on github -- id: ENC[AES256_GCM,data:YuXA/C5Fp8rM,iv:alC8xmJt/ZIm363nPJj4rfW3jzpO0Ayzs3C/6DKsLw4=,tag:TbW68nbIc8nWUXAzm43T/g==,type:str] - type: ENC[AES256_GCM,data:cq6IUTmqEUkXTspgLCAU9g==,iv:Wibz+He3qhJDpaaYd0Xw5hR8PXjLvVZO+vsVrXO8e7E=,tag:4GgFHMou/vXN+dsNSvJTiQ==,type:str] - privatekey: ENC[AES256_GCM,data:QmtzfvvhKoXqfTwQEtbo/pl1qkTBaPw6SrEW5YcgK17/TdHENdwpbFsO10oXjI9Rx6/ZInsMnlJu3bBr3r4zczi66NTMnPGauP6OvmFcve2uBySxgntRzZdlNw8LIe1q1OxqqBRIaxdczEi2wljS2O6Vcw8lHVPU3Oxn8N1s2+wmIRlMl3Vuh0rTAyFRz63caM0gm9hN0cphNeKpLF+vRsv9HG1rsMJvCnmbiENZuMYYdpvca7Nq8cjTRTPmtiTVsJNIvQ5PK4AFxzXvrCewFgnRhPW/A5ffemRhmSoeK1btFiknOlXul8hyyGY3nijQB98pkPZZvAP94IQohakJ3Mn/5OEumVFP4LQeCD/aq2XGFktKrRDRSwIhzx/M2ruocoRzskOfWeKqvR5Kaj3PnMYLkui7sZ99D4MU3CHMngsDA7T10PsiZIqeeFjPfz1Ji/EC4pbhZtPUymBySb8qpnlZ5VXV4/I=,iv:PJr0ulGzdbRQoNZRw5jhJIZI+C2J7rTZ1e5SA+TXa3U=,tag:0juOKAxzeByYnyRZp1cIEA==,type:str] - x5u: ENC[AES256_GCM,data:dC/4tDUqa5dKjMcSRExWBupBrwVRPSyTkqevg3mvG58f8AWueoN/bSfxyXz+9Jo6XFOuurFLg3tIDR4q9ZIpq29BdbcPi2pRsplGkSB37U2mRt4oEQ21/04uTC2SOJoZz+rFUd5vE329f4yP/WBHrguX6WJUMvtvA72Y2il71UfVVpuoLdalbS2K,iv:6f6I/fS+1tNC22QUn1l9pmuls6Am1MznAVoLTN1WgZ8=,tag:KKGVxnjxFY1bv6Abl+KPDw==,type:str] -- id: ENC[AES256_GCM,data:V/NvC26R3B1hfIThKgv1QNw=,iv:7rgwJiGibHesit6Ogd796VPV3SHvXn2NXz+RuaFUY3c=,tag:5PNnJ9Ova4V0oQYL9t+cEA==,type:str] - type: ENC[AES256_GCM,data:/ssf,iv:KSEfkKyst/kqeJjZEsG8JafWW+SUc21vLeuMuSBzAp4=,tag:YCEhmrkq5iQoy4KdRFuByw==,type:str] - # The signing parameters for each type of add-on are - # 'add-on' are signed with the OU 'Production' and the provided ID - # 'extension' are signed with the OU 'Mozilla Extensions' and the provided ID - # 'system add-on' are signed with the OU 'Mozilla Components' and the provided ID - # 'hotfix' are signed with the OU 'Production' and the ID 'firefox-hotfix@mozilla.org' - category: ENC[AES256_GCM,data:XTOU9Nm/,iv:Whs8yeRb1A5C4oSJCQ3YsqdeRlDiEwH/zg6M0bR/Slw=,tag:zffohwS8wnD8NKlK1eee8A==,type:str] - certificate: ENC[AES256_GCM,data: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,iv:kDq4suHzB8TbkWsEWowkqUyCR1jXBaU1q+ZjDPd2Z1U=,tag:TKo9dTsqTRn97NriCTSqjQ==,type:str] - privatekey: ENC[AES256_GCM,data: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,iv:i+y6E5Uy0IunC5ctciqt+zwT7y12Jn094h+CXF0IQS4=,tag:PK40bv5CvRdLyHI4dvdInw==,type:str] -- id: ENC[AES256_GCM,data:1wkyriNgFu89eVQ+XO8wvA==,iv:BjZRqMTpXhLbcAV0rZom/Y4JlEDCYHe5KLCiEw1hEkQ=,tag:bJdM0O7YMjyCmglm3gmcdQ==,type:str] - type: ENC[AES256_GCM,data:w94R,iv:RlytY7Ba5UbgnBS9+R42qK+t+Xe0sN/uhNSxxT6DOqM=,tag:CBNRKxuymrQjjvke3Wg1IQ==,type:str] - category: ENC[AES256_GCM,data:ybUGQsC7C8GM,iv:nXBJO0rsGz+iKN29F3xlIjrJckKweJc4Qo/0rse85RU=,tag:7EG/W3v5SQ8iSALvB402dg==,type:str] - certificate: ENC[AES256_GCM,data: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,iv:6vhrkC1fBOUR8f8XLIFSTlBUponmc++p2XD/g8E7iZU=,tag:FdjQzKDMRofELsrkYoT5bQ==,type:str] - privatekey: ENC[AES256_GCM,data:16y5POvqCTSWOqc13yQvC/hSOrIIC/vegRl5iWCp+w+dmFhuynMvy3dnT3X3pnIR125sWy5iJFTOT3pvztn/QmZI6KFkA4ZNjUdAvRLBLxTNAksG/Tjil9bD5QwluBQkVcN0gv1sqXYg41lQj2nM+QGKGLZzuMC9v0sfyg5yGFhj8omBnNelfrcqpMmYE3en5PBKIMsakL83LT34kO0WaMbbGf8fvVVAG1OoGiSTlURRGzKKbIFFqF3w8kFc3yM5CBGOO+z34jKyzo+4w1JtSnTM9x1jUbqiMZasCBcHuiPV+ZE5LnNZsti7oIISgpRNAqhLn7Ew6i2SBMfucJ6K8twuL311WyPUBMqCfxkfcaW1Pz0V+uyvsWUREX3x4kcpS0Aru2q5THPM9VrV9TcLmlTRx3SeM0wiy6B4qumsVMpZ119yv0bjLe5FIuS5Sr7xV/Hn5hntbcll+uANylUcxB7Sovaoirs=,iv:AdNknN9JZU2Ui7iUiloV/U3BFb9RoGke4Af7NK7Th5Y=,tag:95g3P6/NZJgJQooE7rRdWw==,type:str] -authorizations: -- id: ENC[AES256_GCM,data:Ize7mec=,iv:wt2q5cv7h45x/Dqv8TTV3YEKW50GqjBaVWiilEoAS6I=,tag:KXzd0J9FSEAvfoOiP3af9g==,type:str] - key: ENC[AES256_GCM,data:2GUibJZH1h3yhyT1wAVrEQO8VMdWGXOPUBvsY3vxVUrHfhFwvuhb2442yS4LXWdQsXI=,iv:GqLf7VMOeq3Q2ejfUCeoF11ud0/s1YHzXf/YYr62Y0I=,tag:uHzjtPke1NYJuqse6jCgHA==,type:str] - signers: - - ENC[AES256_GCM,data:hhiKXqFEWw==,iv:9eqeez1pdptOnQSbfANhoNfQ2c/r9mT/7h9Pf9pBVso=,tag:gG7Ai2ND31mhfnbkoz3E5w==,type:str] - - ENC[AES256_GCM,data:DxdtBbiVlA==,iv:rbf12CdjTfXPgMt2TAA+gr+nJFaTm5GARfXbhgQs+FE=,tag:687Ci2gnKH8xMbQVokSbHw==,type:str] - - ENC[AES256_GCM,data:vt7Lee6wjg==,iv:qGSpnQ9qPZebf8fP37xmgHgpi/muGn6bkBNBC0GWEVU=,tag:q08RuOgqY0K4kL+dXm9iYQ==,type:str] - - ENC[AES256_GCM,data:IpR/ONCEGrIH,iv:P9CIKm7HqS1ntBdSAHvLcXbJU57b4A8dKYIp9ig0Yc0=,tag:xwb5aUjrerM9EQp2afF0nw==,type:str] - - ENC[AES256_GCM,data:c7RtOItd7qmO632afREsQSw=,iv:7u4SGmJvIsO9F5pSik5K5u/vIUfXy2HYv6hqSy7oS6s=,tag:8d/wNh54ycIqBmnTKNrLXg==,type:str] - - ENC[AES256_GCM,data:jQ6HrRhnhFoC56i0PjrcvQ==,iv:q9NW4YYPpSMop9om3X7TMTlkhAb75OPGIFObFy3ATKM=,tag:zMN/RH2cqLWwJai7JPrCbA==,type:str] -- id: ENC[AES256_GCM,data:PWlI,iv:FgI79j3a/GdBeGPFxiG6qR5s+yebmAaHe0nQkAWclO0=,tag:dvHl9E6NWcdvDziUWb8GXw==,type:str] - key: ENC[AES256_GCM,data:pO7djyj7EifPtEKcco989pOnfc37I2ohRX+X9QNVwOvndMne8Vrcuc6xBbrSgOKiNgOQ,iv:RUxHaqepYcxlXS1DvccUewXP07jJL2od3mVO0wiRtf0=,tag:qbI5m9YocyFkXC92iayoDA==,type:str] - signers: - - ENC[AES256_GCM,data:XjrOeoqjCQ==,iv:ExdK/XluUn2jvpmLmkkEgTTma1WSgGyOl3syt3+jTmU=,tag:VMdykqn+z3VyJQDx+Hnrag==,type:str] -- id: ENC[AES256_GCM,data:wNtyKvy1rKa2,iv:NpJ581YwhcKEHFgFJ2raxS6jKRBmlKbHwrU6sOH6kGI=,tag:pLp8C2mGmsPvlnD3uhB0Ng==,type:str] - key: ENC[AES256_GCM,data:PLOuIDpX5A6IWBDJtg7hzRu0yEK+glRbt4CZKAHArKxveL9TqkdDY9Yb3KZO2FYTKlQ=,iv:Nqn2OpvI1suCLOv1fx98OpA4sXdUpplE8iWx5oxzxlU=,tag:6TT4h6nn/n6rn/JyztuBGA==,type:str] - signers: - - ENC[AES256_GCM,data:JdaKRRpG85uR,iv:SaxQlD4DdTPjS0Mxchjp7kdHjTzmXwRCeJg5Ihvxu9w=,tag:4f09ZYawmMiVgbCuandz/Q==,type:str] -monitoring: - key: ENC[AES256_GCM,data:G1+NNWVMazWHIDI00cV2nepWWk8j+SxnXZW/bdJW6KnTCz0u4AaAEPbxgKA9AzpNrl/i,iv:9Fy4RCHGnACdtZYyNb+2tY5By2IZ5WoIEA35pk//rSo=,tag:nmnc9Vu8fgfOfMQIqdCuMQ==,type:str] -sops: - kms: [] - lastmodified: '2017-03-23T18:27:31Z' - mac: ENC[AES256_GCM,data:PHUT3s0tQuW4ndA621MvDdKO2Ja8jVoLQ2CzSNu2S8sKp33UvQp7dNuJxFH+w2REdNEt3onId7b6MdXhIF2tWQmUJEzX5mnOkx62atA7i4BJ1l8+cudrSmlmJwK+gYravolJyKngF7l1kozdKBBmGZlBLJYrDGzcMzEA827ydX8=,iv:vbs+HhVgBo18ttApQTOAWBCz98txm3NFj5Z96obcYEU=,tag:WtvfaS080scHmmx61cEUUQ==,type:str] - pgp: - - created_at: '2017-03-23T18:27:05Z' - enc: |- - -----BEGIN PGP MESSAGE----- - - wYwDEEVDpnzXnMABBACN1HrTHm2h3GY58UghGAA0DA5YV4FpvrQWg0ny14G3wxpZ - a+ynM4pfw+kFRE/i+M3/g9JInWQC3yND/VS75bqdac7MCccUVe4ubWNtCthgrlDL - xcNFCbQ9shbuGVpfgwvWBk399ywDmC8T2lWeBtdfHAgtzdETFsknX+ECJUULktLg - AeSznL/9OGkloifBTigV1DTe4ZgH4N7gUOGTzOCn4qH2USzgC+UGmLzpwhroUj4V - aWQdsokeaxIpW9gsUYwzHl2qaU6pCuDy5Ii7ubIQTkWtsCcXsG1WbJLiTucRcOGU - xAA= - =B8Yw - -----END PGP MESSAGE----- - fp: 1022470DE3F0BC54BC6AB62DE05550BC07FB1A0A - unencrypted_suffix: _unencrypted - version: 2.0.8 diff --git a/bin/run_integration_tests.sh b/bin/run_integration_tests.sh index 72a74fe57..0362e88e7 100755 --- a/bin/run_integration_tests.sh +++ b/bin/run_integration_tests.sh @@ -28,7 +28,7 @@ while test "true" != "$(docker inspect -f {{.State.Running}} autograph-app-hsm)" done # fetch the updated root hash from the app-hsm service -APP_HSM_NORMANDY_ROOT_HASH=$(docker compose exec app-hsm yq -r '.signers[] | select(.id == "normandy").cacert' /app/autograph.softhsm.yaml | \ +APP_HSM_NORMANDY_ROOT_HASH=$(docker compose exec app-hsm yq -r '.signers[] | select(.id == "normandy").cacert' /app/autograph.softhsm-signer.yaml | \ openssl x509 -outform DER | sha256sum | awk '{print $1}') # start the monitor containers diff --git a/docker-compose.yml b/docker-compose.yml index 176a16d9a..ccb2757c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,7 +58,9 @@ services: "--", "autograph", "-c", - "/app/autograph.softhsm.yaml", + "/app/autograph.softhsm-service.yaml", + "-s", + "/app/autograph.softhsm-signer.yaml", ] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8001/__heartbeat__"] diff --git a/docs/architecture.md b/docs/architecture.md index d386e1c21..e29a95b56 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -65,7 +65,7 @@ signer, and returns the encoded signature back to the client. The authentication/authorization model is probably the most complex part of the autograph core. Clients are required to provide a Hawk authorization with payload signature issued by a user trusted by -autograph. The `authorization` section of the autograph.yaml +autograph. The `authorization` section of the autograph-signer.yaml configuration lists permitted users, along with the signers each is allowed to use. diff --git a/docs/configuration.md b/docs/configuration.md index b9acbe8ae..c10875a50 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,8 +1,8 @@ # Configuration -The configuration lives in `autograph.yaml` at the default path -`/etc/autograph/autograph.yaml` (use the flag `-c` to provide an -alternate path). +The configuration lives in `autograph-service.yaml` and `autograph-signer.yaml` +at the default path `/etc/autograph/` (use the flags `-c` and `-s` to provide +alternate paths). ## Server @@ -152,7 +152,7 @@ The binary is located in `$GOPATH/bin/autograph` and can be started with the configuration file: ``` bash -$ $GOPATH/bin/autograph -c autograph.yaml +$ $GOPATH/bin/autograph -c autograph-service.yaml -s autograph-signer.yaml {"Timestamp":1453721399358695130,"Type":"app.log","Logger":"Autograph","Hostname":"gator1","EnvVersion":"2.0","Pid":17287,"Fields":{"msg":"main.go:74: Starting Autograph API on localhost:8000"}} ``` diff --git a/docs/hsm.md b/docs/hsm.md index e82456793..323f8211a 100644 --- a/docs/hsm.md +++ b/docs/hsm.md @@ -93,5 +93,5 @@ signers: Note that autograph does not generate slots or keys, this must be handled separately. For a full working example, take a look at -`autograph.softhsm.yaml` and how it is used by CircleCI in +`autograph.softhsm-signer.yaml` and how it is used by CircleCI in `.circleci/config.yaml`. diff --git a/handlers_racing_test.go b/handlers_racing_test.go index da833d181..1af05563a 100644 --- a/handlers_racing_test.go +++ b/handlers_racing_test.go @@ -25,7 +25,7 @@ import ( func TestSignaturePass(t *testing.T) { t.Parallel() - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) var TESTCASES = []struct { endpoint string @@ -111,7 +111,7 @@ func TestSignaturePass(t *testing.T) { }, } for i, testcase := range TESTCASES { - userid := conf.Authorizations[0].ID + userid := signerConf.Authorizations[0].ID body, err := json.Marshal(testcase.signaturerequests) if err != nil { t.Fatal(err) diff --git a/handlers_test.go b/handlers_test.go index acaafb1cf..e37c9c26a 100644 --- a/handlers_test.go +++ b/handlers_test.go @@ -129,7 +129,7 @@ func (testcase *HandlerTestCase) Run(ag *autographer, t *testing.T, handler func } func TestBadRequest(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) var TESTCASES = []struct { endpoint string @@ -212,7 +212,7 @@ func TestBadRequest(t *testing.T) { t.Fatal(err) } req.Header.Set("Content-Type", "application/json") - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -235,7 +235,7 @@ func TestBadRequest(t *testing.T) { } func TestRequestJustLargeEnough(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) os.Setenv("MAX_BODY_SIZE_IN_MB", "1") blob := strings.Repeat("foobar", 166666) @@ -245,7 +245,7 @@ func TestRequestJustLargeEnough(t *testing.T) { t.Fatal(err) } req.Header.Set("Content-Type", "application/json") - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -271,7 +271,7 @@ func TestRequestJustLargeEnough(t *testing.T) { } func TestRequestTooLarge(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) os.Setenv("MAX_BODY_SIZE_IN_MB", "1") blob := strings.Repeat("foobar", 166667) @@ -281,7 +281,7 @@ func TestRequestTooLarge(t *testing.T) { t.Fatal(err) } req.Header.Set("Content-Type", "application/json") - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -306,7 +306,7 @@ func TestRequestTooLarge(t *testing.T) { } func TestBadContentType(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) blob := "foofoofoofoofoofoofoofoofoofoofoofoofoofoo" body := strings.NewReader(blob) @@ -315,7 +315,7 @@ func TestBadContentType(t *testing.T) { t.Fatal(err) } req.Header.Set("Content-Type", "application/foobar") - auth, err := ag.getAuthByID(conf.Authorizations[0].ID) + auth, err := ag.getAuthByID(signerConf.Authorizations[0].ID) if err != nil { t.Fatal(err) } @@ -335,7 +335,7 @@ func TestBadContentType(t *testing.T) { } func TestAuthFail(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) var TESTCASES = []struct { user string @@ -415,7 +415,7 @@ func checkHeartbeatReturnsExpectedStatusAndBody(ag *autographer, t *testing.T, n } func TestHeartbeat(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) ag.heartbeatConf = &heartbeatConfig{} var TESTCASES = []struct { @@ -435,7 +435,7 @@ func TestHeartbeat(t *testing.T) { } func TestHeartbeatChecksHSMStatusFails(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) // NB: do not run in parallel with TestHeartbeat* ag.heartbeatConf = &heartbeatConfig{ HSMCheckTimeout: time.Second, @@ -448,7 +448,7 @@ func TestHeartbeatChecksHSMStatusFails(t *testing.T) { } func TestHeartbeatChecksHSMStatusFailsWhenNotConfigured(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) // NB: do not run in parallel with TestHeartbeat* expectedStatus := http.StatusInternalServerError expectedBody := []byte("Missing heartbeat config\r\nrequest-id: -\n") @@ -456,7 +456,7 @@ func TestHeartbeatChecksHSMStatusFailsWhenNotConfigured(t *testing.T) { } func TestHeartbeatChecksDBStatusOKAndTimesout(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) // NB: do not run in parallel with TestHeartbeat* or DB tests host := database.GetTestDBHost() db, err := database.Connect(database.Config{ @@ -523,39 +523,39 @@ func TestVersion(t *testing.T) { // * `appkey1` and `appkey2` for `alice` // * `appkey2` only for `bob` func TestSignerAuthorized(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) var TESTCASES = []struct { userid string sgs []formats.SignatureRequest }{ { - userid: conf.Authorizations[0].ID, + userid: signerConf.Authorizations[0].ID, sgs: []formats.SignatureRequest{ formats.SignatureRequest{ Input: "PCFET0NUWVBFIEhUTUw+CjxodG1sPgo8IS0tIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTEyMjY5MjggLS0+CjxoZWFkPgogIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICA8dGl0bGU+VGVzdHBhZ2UgZm9yIGJ1ZyAxMjI2OTI4PC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KICBKdXN0IGEgZnVsbHkgZ29vZCB0ZXN0cGFnZSBmb3IgQnVnIDEyMjY5Mjg8YnIvPgo8L2JvZHk+CjwvaHRtbD4K", - KeyID: conf.Authorizations[0].Signers[0], + KeyID: signerConf.Authorizations[0].Signers[0], }, formats.SignatureRequest{ Input: "y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d", - KeyID: conf.Authorizations[0].Signers[0], + KeyID: signerConf.Authorizations[0].Signers[0], }, formats.SignatureRequest{ Input: "Q29udGVudC1TaWduYXR1cmU6ADwhRE9DVFlQRSBIVE1MPgo8aHRtbD4KPCEtLSBodHRwczovL2J1Z3ppbGxhLm1vemlsbGEub3JnL3Nob3dfYnVnLmNnaT9pZD0xMjI2OTI4IC0tPgo8aGVhZD4KICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CiAgPHRpdGxlPlRlc3RwYWdlIGZvciBidWcgMTIyNjkyODwvdGl0bGU+CjwvaGVhZD4KPGJvZHk+CiAgSnVzdCBhIGZ1bGx5IGdvb2QgdGVzdHBhZ2UgZm9yIEJ1ZyAxMjI2OTI4PGJyLz4KPC9ib2R5Pgo8L2h0bWw+Cg==", - KeyID: conf.Authorizations[0].Signers[1], + KeyID: signerConf.Authorizations[0].Signers[1], }, }, }, { - userid: conf.Authorizations[1].ID, + userid: signerConf.Authorizations[1].ID, sgs: []formats.SignatureRequest{ formats.SignatureRequest{ Input: "PCFET0NUWVBFIEhUTUw+CjxodG1sPgo8IS0tIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTEyMjY5MjggLS0+CjxoZWFkPgogIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICA8dGl0bGU+VGVzdHBhZ2UgZm9yIGJ1ZyAxMjI2OTI4PC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KICBKdXN0IGEgZnVsbHkgZ29vZCB0ZXN0cGFnZSBmb3IgQnVnIDEyMjY5Mjg8YnIvPgo8L2JvZHk+CjwvaHRtbD4K", - KeyID: conf.Authorizations[1].Signers[0], + KeyID: signerConf.Authorizations[1].Signers[0], }, formats.SignatureRequest{ Input: "y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d", - KeyID: conf.Authorizations[1].Signers[0], + KeyID: signerConf.Authorizations[1].Signers[0], }, }, }, @@ -613,20 +613,20 @@ func TestSignerAuthorized(t *testing.T) { // verify that user `bob` is not allowed to sign with `appkey1` func TestSignerUnauthorized(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) var TESTCASES = []formats.SignatureRequest{ // request signature that need to prepend the content-signature:\x00 header formats.SignatureRequest{ Input: "PCFET0NUWVBFIEhUTUw+CjxodG1sPgo8IS0tIGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTEyMjY5MjggLS0+CjxoZWFkPgogIDxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4KICA8dGl0bGU+VGVzdHBhZ2UgZm9yIGJ1ZyAxMjI2OTI4PC90aXRsZT4KPC9oZWFkPgo8Ym9keT4KICBKdXN0IGEgZnVsbHkgZ29vZCB0ZXN0cGFnZSBmb3IgQnVnIDEyMjY5Mjg8YnIvPgo8L2JvZHk+CjwvaHRtbD4K", - KeyID: conf.Authorizations[0].Signers[0], + KeyID: signerConf.Authorizations[0].Signers[0], }, formats.SignatureRequest{ Input: "y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d", - KeyID: conf.Authorizations[0].Signers[0], + KeyID: signerConf.Authorizations[0].Signers[0], }, } - userid := conf.Authorizations[1].ID + userid := signerConf.Authorizations[1].ID body, err := json.Marshal(TESTCASES) if err != nil { t.Fatal(err) @@ -652,14 +652,14 @@ func TestSignerUnauthorized(t *testing.T) { } func TestContentType(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, _, signerConf := newTestAutographer(t) var TESTCASES = []formats.SignatureRequest{ formats.SignatureRequest{ Input: "Y2FyaWJvdXZpbmRpZXV4Cg==", }, } - userid := conf.Authorizations[0].ID + userid := signerConf.Authorizations[0].ID body, err := json.Marshal(TESTCASES) if err != nil { t.Fatal(err) @@ -686,7 +686,7 @@ func TestContentType(t *testing.T) { } func TestDebug(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) ag.enableDebug() if !ag.debug { t.Fatalf("expected debug mode to be enabled, but is disabled") @@ -698,7 +698,7 @@ func TestDebug(t *testing.T) { } func TestHandleGetAuthKeyIDs(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) const autographDevAliceKeyIDsJSON = "[\"apk_cert_with_ecdsa_sha256\",\"apk_cert_with_ecdsa_sha256_v3\",\"appkey1\",\"appkey2\",\"dummyrsa\",\"dummyrsapss\",\"extensions-ecdsa\",\"extensions-ecdsa-expired-chain\",\"legacy_apk_with_rsa\",\"normandy\",\"pgpsubkey\",\"pgpsubkey-debsign\",\"randompgp\",\"randompgp-debsign\",\"randompgp-rpmsign\",\"remote-settings\",\"testapp-android\",\"testapp-android-legacy\",\"testapp-android-v3\",\"testauthenticode\",\"testmar\",\"testmarecdsa\",\"webextensions-rsa\",\"webextensions-rsa-with-recommendation\"]" diff --git a/libkmsp11-config.yaml b/libkmsp11-config.yaml new file mode 100644 index 000000000..0832b7e88 --- /dev/null +++ b/libkmsp11-config.yaml @@ -0,0 +1,3 @@ +tokens: + - key_ring: projects/moz-fx-autograph-prod/locations/global/keyRings/autograph-runtime-key-ring-prod + label: gcp-token diff --git a/main.go b/main.go index 632edab41..32d224ac7 100644 --- a/main.go +++ b/main.go @@ -45,8 +45,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) -// configuration loads a yaml file that contains the configuration of Autograph -type configuration struct { +type serviceConfig struct { Server struct { Listen string NonceCacheSize int @@ -54,21 +53,23 @@ type configuration struct { ReadTimeout time.Duration WriteTimeout time.Duration } - // DebugServer are the settings for the control plane HTTP server where // metrics are exposed for collection and some limited utitilites can live. - DebugServer debugServerConfig `yaml:"debugserver"` - + DebugServer debugServerConfig `yaml:"debugserver"` HSM crypto11.PKCS11Config Database database.Config - Signers []signer.Configuration - Authorizations []authorization Monitoring authorization Heartbeat heartbeatConfig HawkTimestampValidity string MonitorInterval time.Duration } +// configuration loads a yaml file that contains the configuration of Autograph +type signerConfig struct { + Signers []signer.Configuration + Authorizations []authorization +} + type debugServerConfig struct { // Listen is a TCP address for the HTTP server to listen on. See // http.Server's Addr field for details. @@ -98,16 +99,18 @@ func main() { run(parseArgsAndLoadConfig(args)) } -func parseArgsAndLoadConfig(args []string) (conf configuration, listen string, debug bool) { +func parseArgsAndLoadConfig(args []string) (serviceConf serviceConfig, signerConf signerConfig, listen string, debug bool) { var ( - cfgFile string - port string - err error - logLevel string - fset = flag.NewFlagSet("parseArgsAndLoadConfig", flag.ContinueOnError) + serviceFile string + signerFile string + port string + err error + logLevel string + fset = flag.NewFlagSet("parseArgsAndLoadConfig", flag.ContinueOnError) ) - fset.StringVar(&cfgFile, "c", "autograph.yaml", "Path to configuration file") + fset.StringVar(&serviceFile, "c", "autograph-service.yaml", "Path to service configuration file") + fset.StringVar(&signerFile, "s", "autograph-signer.yaml", "Path to signer configuration file") fset.StringVar(&port, "p", "", "Port to listen on. Overrides the listen var from the config file") // https://github.com/sirupsen/logrus#level-logging fset.StringVar(&logLevel, "l", "", "Set the logging level. Optional defaulting to info. Options: trace, debug, info, warning, error, fatal and panic") @@ -138,22 +141,26 @@ func parseArgsAndLoadConfig(args []string) (conf configuration, listen string, d log.Infof("Set logging level to %s", level) } - err = conf.loadFromFile(cfgFile) + err = serviceConf.loadFromFile(serviceFile) + if err != nil { + log.Fatal(err) + } + err = signerConf.loadFromFile(signerFile) if err != nil { log.Fatal(err) } - confListen := strings.Split(conf.Server.Listen, ":") + confListen := strings.Split(serviceConf.Server.Listen, ":") if len(confListen) > 1 && port != "" && port != confListen[1] { listen = fmt.Sprintf("%s:%s", confListen[0], port) - log.Infof("Overriding listen addr from config %s with new port from the commandline: %s", conf.Server.Listen, listen) + log.Infof("Overriding listen addr from config %s with new port from the commandline: %s", serviceConf.Server.Listen, listen) } else { - listen = conf.Server.Listen + listen = serviceConf.Server.Listen } return } -func run(conf configuration, listen string, debug bool) { +func run(serviceConf serviceConfig, signerConf signerConfig, listen string, debug bool) { var ( ag *autographer err error @@ -161,37 +168,37 @@ func run(conf configuration, listen string, debug bool) { // initialize signers from the configuration // and store them into the autographer handler - ag = newAutographer(conf.Server.NonceCacheSize) - ag.heartbeatConf = &conf.Heartbeat + ag = newAutographer(serviceConf.Server.NonceCacheSize) + ag.heartbeatConf = &serviceConf.Heartbeat - if conf.Database.Name != "" { + if serviceConf.Database.Name != "" { // ignore the monitor close chan since it will stop // when the app is stopped - _ = ag.addDB(conf.Database) + _ = ag.addDB(serviceConf.Database) } // initialize the hsm if a configuration is defined - if conf.HSM.Path != "" { - err = ag.initHSM(conf) + if serviceConf.HSM.Path != "" { + err = ag.initHSM(serviceConf, signerConf) if err != nil { log.Fatalf("main.run: %s", err) } } - err = ag.addSigners(conf.Signers) + err = ag.addSigners(signerConf.Signers) if err != nil { log.Fatal(err) } - err = ag.addAuthorizations(conf.Authorizations) + err = ag.addAuthorizations(signerConf.Authorizations) if err != nil { log.Fatal(err) } - err = ag.addMonitoring(conf.Monitoring) + err = ag.addMonitoring(serviceConf.Monitoring) if err != nil { log.Fatal(err) } - if conf.HawkTimestampValidity != "" { - ag.hawkMaxTimestampSkew, err = time.ParseDuration(conf.HawkTimestampValidity) + if serviceConf.HawkTimestampValidity != "" { + ag.hawkMaxTimestampSkew, err = time.ParseDuration(serviceConf.HawkTimestampValidity) if err != nil { log.Fatal(err) } @@ -207,7 +214,7 @@ func run(conf configuration, listen string, debug bool) { ag.startCleanupHandler() // Initialize a monitor. - monitor := newMonitor(ag, conf.MonitorInterval) + monitor := newMonitor(ag, serviceConf.MonitorInterval) router := mux.NewRouter().StrictSlash(true) router.HandleFunc("/__heartbeat__", statsMiddleware(ag.handleHeartbeat, "http.nonapi.heartbeat")).Methods("GET") @@ -222,19 +229,19 @@ func run(conf configuration, listen string, debug bool) { // For each signer with a local chain upload location (eg: using the file // scheme) create an handler to serve that directory at the path /x5u/keyid/ - for _, signerConf := range conf.Signers { - parsedURL, err := url.Parse(signerConf.X5U) + for _, signer := range signerConf.Signers { + parsedURL, err := url.Parse(signer.X5U) if err != nil || parsedURL.Scheme != "file" { // This signer doesn't upload certificate chains to local storage. continue } - prefix := fmt.Sprintf("/x5u/%s/", signerConf.ID) + prefix := fmt.Sprintf("/x5u/%s/", signer.ID) router.PathPrefix(prefix).Handler(http.StripPrefix(prefix, http.FileServer(http.Dir(parsedURL.Path)))) } - if conf.DebugServer.Listen != "" { - log.Infof("starting debug server on %s", conf.DebugServer.Listen) + if serviceConf.DebugServer.Listen != "" { + log.Infof("starting debug server on %s", serviceConf.DebugServer.Listen) go func() { mux := mux.NewRouter() mux.Handle("/metrics", promhttp.Handler()) @@ -249,24 +256,24 @@ func run(conf configuration, listen string, debug bool) { } srv := http.Server{ - Addr: conf.DebugServer.Listen, - IdleTimeout: conf.Server.IdleTimeout, - ReadTimeout: conf.Server.ReadTimeout, - WriteTimeout: conf.Server.WriteTimeout, + Addr: serviceConf.DebugServer.Listen, + IdleTimeout: serviceConf.Server.IdleTimeout, + ReadTimeout: serviceConf.Server.ReadTimeout, + WriteTimeout: serviceConf.Server.WriteTimeout, Handler: mux, } err := srv.ListenAndServe() if err != nil { - log.Fatalf("unable to start up debug server on %s: %s", conf.DebugServer.Listen, err) + log.Fatalf("unable to start up debug server on %s: %s", serviceConf.DebugServer.Listen, err) } }() } server := &http.Server{ - IdleTimeout: conf.Server.IdleTimeout, - ReadTimeout: conf.Server.ReadTimeout, - WriteTimeout: conf.Server.WriteTimeout, + IdleTimeout: serviceConf.Server.IdleTimeout, + ReadTimeout: serviceConf.Server.ReadTimeout, + WriteTimeout: serviceConf.Server.WriteTimeout, Addr: listen, Handler: handleMiddlewares( router, @@ -276,7 +283,7 @@ func run(conf configuration, listen string, debug bool) { logRequest(), ), } - log.Infof("starting autograph on %s with timeouts: idle %s read %s write %s", listen, conf.Server.IdleTimeout, conf.Server.ReadTimeout, conf.Server.WriteTimeout) + log.Infof("starting autograph on %s with timeouts: idle %s read %s write %s", listen, serviceConf.Server.IdleTimeout, serviceConf.Server.ReadTimeout, serviceConf.Server.WriteTimeout) err = server.ListenAndServe() if err != nil { log.Fatal(err) @@ -286,8 +293,30 @@ func run(conf configuration, listen string, debug bool) { close(ag.exit) } -// loadFromFile reads a configuration from a local file -func (c *configuration) loadFromFile(path string) error { +// read a serviceConfig from a local file +func (c *serviceConfig) loadFromFile(path string) error { + var ( + data []byte + err error + ) + data, err = os.ReadFile(path) + if err != nil { + return err + } + + err = yaml.Unmarshal(data, &c) + if err != nil { + return err + } + + if c.Heartbeat.DBCheckTimeout == time.Duration(int64(0)) || c.Heartbeat.HSMCheckTimeout == time.Duration(int64(0)) { + return fmt.Errorf("missing required heartbeat config section with non-zero timeouts") + } + return nil +} + +// read a possibly SOPS encrypted signerConfig from a local file +func (c *signerConfig) loadFromFile(path string) error { var ( data, confData []byte confSHA [32]byte @@ -318,10 +347,6 @@ func (c *configuration) loadFromFile(path string) error { if err != nil { return err } - - if c.Heartbeat.DBCheckTimeout == time.Duration(int64(0)) || c.Heartbeat.HSMCheckTimeout == time.Duration(int64(0)) { - return fmt.Errorf("missing required heartbeat config section with non-zero timeouts") - } return nil } @@ -401,8 +426,8 @@ func (a *autographer) addDB(dbConf database.Config) chan bool { } // initHSM sets up the HSM and notifies signers it is available -func (a *autographer) initHSM(conf configuration) error { - tmpCtx, err := crypto11.Configure(&conf.HSM, crypto11.NewDefaultPKCS11Context) +func (a *autographer) initHSM(serviceConf serviceConfig, signerConf signerConfig) error { + tmpCtx, err := crypto11.Configure(&serviceConf.HSM, crypto11.NewDefaultPKCS11Context) if err != nil { return fmt.Errorf("error in initHSM from crypto11.Configure: %w", err) } @@ -415,7 +440,7 @@ func (a *autographer) initHSM(conf configuration) error { } // if we successfully initialized the crypto11 context, // tell the signers they can try using the HSM - for i := range conf.Signers { + for i := range signerConf.Signers { // These two lines are strange and required until we fix how we use // `signer.Configuration`. Since this list of // `singer.Configuration`s is only of structs (not pointers), and @@ -430,8 +455,8 @@ func (a *autographer) initHSM(conf configuration) error { // TODO(AUT-203): when we make `signer.Configuration` immutable, // we'll not need this strange `conf.Signers[i]` and can loop // through them normally. - conf.Signers[i].InitHSM(hsm) - signerConf := &conf.Signers[i] + signerConf.Signers[i].InitHSM(hsm) + signerConf := &signerConf.Signers[i] if signerConf.PrivateKeyHasPEMPrefix() { // If the private key is not stored in the HSM, we have nothing diff --git a/main_racing_test.go b/main_racing_test.go index b26b81d82..22eec377e 100644 --- a/main_racing_test.go +++ b/main_racing_test.go @@ -14,27 +14,27 @@ func TestLogLevelParsing(t *testing.T) { var debug, fatal bool - _, _, debug = parseArgsAndLoadConfig([]string{"-l", "debug"}) + _, _, _, debug = parseArgsAndLoadConfig([]string{"-l", "debug"}) if !(debug == true && log.GetLevel() == log.DebugLevel) { t.Errorf("failed to set debug flag for debug log level") } - _, _, debug = parseArgsAndLoadConfig([]string{"-D"}) + _, _, _, debug = parseArgsAndLoadConfig([]string{"-D"}) if !(debug == true && log.GetLevel() == log.DebugLevel) { t.Errorf("failed to set debug log level for debug flag") } - _, _, debug = parseArgsAndLoadConfig([]string{"-l", "error"}) + _, _, _, debug = parseArgsAndLoadConfig([]string{"-l", "error"}) if !(debug == false && log.GetLevel() == log.ErrorLevel) { t.Errorf("failed to set error log level") } log.StandardLogger().ExitFunc = func(int) { fatal = true } - _, _, _ = parseArgsAndLoadConfig([]string{"-l", "error", "-D"}) + _, _, _, _ = parseArgsAndLoadConfig([]string{"-l", "error", "-D"}) if fatal != true { t.Errorf("did not fail for mismatched log level and debug flag") } fatal = false - _, _, _ = parseArgsAndLoadConfig([]string{"-l", "foo"}) + _, _, _, _ = parseArgsAndLoadConfig([]string{"-l", "foo"}) if fatal != true { t.Errorf("did not fail for invalid log level") } diff --git a/main_test.go b/main_test.go index f2a9e7337..1c7d74f0e 100644 --- a/main_test.go +++ b/main_test.go @@ -15,29 +15,34 @@ import ( log "github.com/sirupsen/logrus" ) -func newTestAutographer(t *testing.T) (*autographer, configuration) { - var conf configuration +func newTestAutographer(t *testing.T) (*autographer, serviceConfig, signerConfig) { + var serviceConf serviceConfig + var signerConf signerConfig // load the signers - err := conf.loadFromFile("autograph.yaml") + err := serviceConf.loadFromFile("autograph-service.yaml") + if err != nil { + log.Fatal(err) + } + err = signerConf.loadFromFile("autograph-signer.yaml") if err != nil { log.Fatal(err) } ag := newAutographer(1) - err = ag.addSigners(conf.Signers) + err = ag.addSigners(signerConf.Signers) if err != nil { log.Fatal(err) } - err = ag.addAuthorizations(conf.Authorizations) + err = ag.addAuthorizations(signerConf.Authorizations) if err != nil { log.Fatal(err) } - err = ag.addMonitoring(conf.Monitoring) + err = ag.addMonitoring(serviceConf.Monitoring) if err != nil { log.Fatal(err) } - if conf.HawkTimestampValidity != "" { - ag.hawkMaxTimestampSkew, err = time.ParseDuration(conf.HawkTimestampValidity) + if serviceConf.HawkTimestampValidity != "" { + ag.hawkMaxTimestampSkew, err = time.ParseDuration(serviceConf.HawkTimestampValidity) if err != nil { log.Fatal(err) } @@ -49,16 +54,16 @@ func newTestAutographer(t *testing.T) (*autographer, configuration) { close(ag.exit) }) - return ag, conf + return ag, serviceConf, signerConf } -func TestConfigLoad(t *testing.T) { +func TestServiceConfigLoad(t *testing.T) { testcases := []struct { name string pass bool data []byte }{ - {"one signer", true, []byte(` + {"simple service", true, []byte(` server: listen: "localhost:8000" noncecachesize: 64 @@ -67,28 +72,62 @@ heartbeat: hsmchecktimeout: 100ms dbchecktimeout: 150ms -signers: - - id: testsigner1 - privatekey: | - -----BEGIN EC PRIVATE KEY----- - MIGkAgEBBDBe7dXZ/epqVkrRWbStmwe2WyTcpWJ5cCbrqcM4tCG4vdX9b0Ri+VYo - LiHkmxenK0mgBwYFK4EEACKhZANiAASvggNRMynXOObY9QW4gJXCwgsNa/8vcjHK - wgzyqfXUzv3PbiZbDVYtYT7FMzd84CmX9BEtsE8bQS2Ci7q0Izp9aRUjCiTlUuAZ - XMhBcGTy1e65CRjbCNM4A8w0/K30x4k= - -----END EC PRIVATE KEY----- - monitoring: key: qowidhqowidhqoihdqodwh `)}, - {"two signers", true, []byte(` + {"missing heartbeat", false, []byte(` server: listen: "localhost:8000" noncecachesize: 64 +`)}, + + {"bogus yaml", false, []byte(`{{{{{{{`)}, + {"yaml with tabs", false, []byte(` +server: + listen: "localhost:8000" + noncecachesize: 64 heartbeat: hsmchecktimeout: 100ms dbchecktimeout: 150ms +`)}, + } + for i, testcase := range testcases { + var conf serviceConfig + // write conf file to /tmp and read it back + fd, err := os.CreateTemp("", "testserviceconf") + if err != nil { + t.Fatal(err) + } + fi, err := fd.Stat() + if err != nil { + t.Fatal(err) + } + filename := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name()) + _, err = fd.Write(testcase.data) + if err != nil { + t.Fatal(err) + } + fd.Close() + err = conf.loadFromFile(filename) + if err != nil && testcase.pass { + t.Fatalf("testcase %d %q failed and should have passed: %v", + i, testcase.name, err) + } + if err == nil && !testcase.pass { + t.Fatalf("testcase %d %q passed and should have failed", i, testcase.name) + } + os.Remove(filename) + } +} +func TestSignerConfigLoad(t *testing.T) { + testcases := []struct { + name string + pass bool + data []byte + }{ + {"one signer", true, []byte(` signers: - id: testsigner1 privatekey: | @@ -98,30 +137,8 @@ signers: wgzyqfXUzv3PbiZbDVYtYT7FMzd84CmX9BEtsE8bQS2Ci7q0Izp9aRUjCiTlUuAZ XMhBcGTy1e65CRjbCNM4A8w0/K30x4k= -----END EC PRIVATE KEY----- - - - id: testsigner2 - privatekey: | - -----BEGIN RSA PRIVATE KEY----- - MIIBOgIBAAJBALhlXvMK5hIgGGRgdUycR8FWAmZC5bOeUrLr9SWep2NnR9nmBDgS - AYYFTraBw2se+oagYyWjccDnbJR9GPHarWkCAwEAAQJAey1kbxCxvhvoj20MDoA7 - QsB02+EGVqWFcvZCjb3c7X4XZS0Oe1y1TJSmyL7oEepuL3NTgXYib+RSLT8vph8u - zQIhANzuVRWzm7sSgTsPgg/P+q/5O2BXzoY/QpWdDb8DWEVjAiEA1apqeW9u38o3 - xpjJBa7tTNzgmuZtupFvB7baO8So0cMCICjTxld3VI0Sk10ltYRUi+AfL7DTKTA3 - 2ocpedPVu2c/AiEAuCx0KQa3sKmTWFmcdYyqOeXuqTbVAMuZxDGGfZxv1JcCIA2v - 84l6Qav0l4A3NDdT+cotbnDqQ5wjF+UZ8uwsBwSl - -----END RSA PRIVATE KEY----- - -authorizations: - - id: tester - key: oiqwhfoqihfoiqeheouqqouhfdq - signers: - - testsigner1 `)}, - {"missing heartbeat config", false, []byte(` -server: - listen: "localhost:8000" - noncecachesize: 64 - + {"two signers", true, []byte(` signers: - id: testsigner1 privatekey: | @@ -143,14 +160,7 @@ signers: 2ocpedPVu2c/AiEAuCx0KQa3sKmTWFmcdYyqOeXuqTbVAMuZxDGGfZxv1JcCIA2v 84l6Qav0l4A3NDdT+cotbnDqQ5wjF+UZ8uwsBwSl -----END RSA PRIVATE KEY----- - -authorizations: - - id: tester - key: oiqwhfoqihfoiqeheouqqouhfdq - signers: - - testsigner1 `)}, - {"bogus yaml", false, []byte(`{{{{{{{`)}, {"yaml with tabs", false, []byte(` server: @@ -173,9 +183,9 @@ authorizations: `)}, } for i, testcase := range testcases { - var conf configuration + var conf signerConfig // write conf file to /tmp and read it back - fd, err := os.CreateTemp("", "autographtestconf") + fd, err := os.CreateTemp("", "testsignerconf") if err != nil { t.Fatal(err) } @@ -202,9 +212,9 @@ authorizations: } func TestDuplicateSigners(t *testing.T) { - var conf configuration + var conf signerConfig // write conf file to /tmp and read it back - fd, err := os.CreateTemp("", "autographtestconf") + fd, err := os.CreateTemp("", "testsignerconf") if err != nil { t.Fatal(err) } @@ -214,14 +224,6 @@ func TestDuplicateSigners(t *testing.T) { } filename := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name()) _, err = fd.Write([]byte(` -server: - listen: "localhost:8000" - noncecachesize: 64 - -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - signers: - id: testsigner1 privatekey: | @@ -246,7 +248,7 @@ signers: } // initialize signers from the configuration // and store them into the autographer handler - dupag := newAutographer(conf.Server.NonceCacheSize) + dupag := newAutographer(64) err = dupag.addSigners(conf.Signers) if err == nil { t.Fatalf("should have failed with duplicate signers but didn't") @@ -255,9 +257,9 @@ signers: } func TestDuplicateAuthorization(t *testing.T) { - var conf configuration + var conf signerConfig // write conf file to /tmp and read it back - fd, err := os.CreateTemp("", "autographtestconf") + fd, err := os.CreateTemp("", "testsignerconf") if err != nil { t.Fatal(err) } @@ -267,14 +269,6 @@ func TestDuplicateAuthorization(t *testing.T) { } filename := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name()) _, err = fd.Write([]byte(` -server: - listen: "localhost:8000" - noncecachesize: 64 - -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - signers: - id: testsigner1 type: contentsignature @@ -310,7 +304,7 @@ authorizations: } // initialize signers from the configuration // and store them into the autographer handler - dupag := newAutographer(conf.Server.NonceCacheSize) + dupag := newAutographer(64) err = dupag.addSigners(conf.Signers) if err != nil { t.Fatal(err) @@ -323,9 +317,9 @@ authorizations: } func TestUnknownSignerInAuthorization(t *testing.T) { - var conf configuration + var conf signerConfig // write conf file to /tmp and read it back - fd, err := os.CreateTemp("", "autographtestconf") + fd, err := os.CreateTemp("", "testsignerconf") if err != nil { t.Fatal(err) } @@ -335,14 +329,6 @@ func TestUnknownSignerInAuthorization(t *testing.T) { } filename := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name()) _, err = fd.Write([]byte(` -server: - listen: "localhost:8000" - noncecachesize: 64 - -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - signers: - id: testsigner1 type: contentsignature @@ -374,7 +360,7 @@ authorizations: } // initialize signers from the configuration // and store them into the autographer handler - ag := newAutographer(conf.Server.NonceCacheSize) + ag := newAutographer(64) err = ag.addSigners(conf.Signers) if err != nil { t.Fatal(err) @@ -388,7 +374,7 @@ authorizations: // An authorization without at least one signer configured must fail func TestAuthWithoutSigner(t *testing.T) { - ag, _ := newTestAutographer(t) + ag, _, _ := newTestAutographer(t) var authorizations = []authorization{ authorization{ @@ -402,17 +388,22 @@ func TestAuthWithoutSigner(t *testing.T) { } func TestConfigLoadFileNotExist(t *testing.T) { - var conf configuration - err := conf.loadFromFile("/tmp/a/b/c/d/e/f/e/d/c/b/a/oned97fy2qoelfahd018oehfa9we8ohf219") + var serviceConf serviceConfig + var signerConf serviceConfig + err := serviceConf.loadFromFile("/tmp/a/b/c/d/e/f/e/d/c/b/a/oned97fy2qoelfahd018oehfa9we8ohf219") + if err == nil { + t.Fatalf("should have file with service config file not found, but passed") + } + err = signerConf.loadFromFile("/tmp/a/b/c/d/e/f/e/d/c/b/a/oned97fy2qoelfahd018oehfa9we8ohf219") if err == nil { - t.Fatalf("should have file with file not found, but passed") + t.Fatalf("should have file with signer config file not found, but passed") } } func TestInvalidSignerConfigDoesNotBlockValidFromLoading(t *testing.T) { - var conf configuration + var conf signerConfig // write conf file to /tmp and read it back - fd, err := os.CreateTemp("", "autographtestconf") + fd, err := os.CreateTemp("", "testsignerconf") if err != nil { t.Fatal(err) } @@ -422,14 +413,6 @@ func TestInvalidSignerConfigDoesNotBlockValidFromLoading(t *testing.T) { } filename := fmt.Sprintf("%s/%s", os.TempDir(), fi.Name()) _, err = fd.Write([]byte(` -server: - listen: "localhost:8000" - noncecachesize: 64 - -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - signers: - id: testsigner1 type: contentsignature @@ -466,7 +449,7 @@ signers: t.Fatalf("config parsing failed and should have passed: %v", err) } - ag := newAutographer(conf.Server.NonceCacheSize) + ag := newAutographer(64) err = ag.addSigners(conf.Signers) if err != nil { t.Fatal(err) @@ -480,7 +463,7 @@ signers: func TestDefaultPort(t *testing.T) { expected := "0.0.0.0:8000" - _, listen, _ := parseArgsAndLoadConfig([]string{}) + _, _, listen, _ := parseArgsAndLoadConfig([]string{}) if listen != expected { t.Errorf("expected listen %s got %s", expected, listen) } @@ -488,7 +471,7 @@ func TestDefaultPort(t *testing.T) { func TestPortOverride(t *testing.T) { expected := "0.0.0.0:8080" - _, listen, _ := parseArgsAndLoadConfig([]string{"-p", "8080"}) + _, _, listen, _ := parseArgsAndLoadConfig([]string{"-p", "8080"}) if listen != expected { t.Errorf("expected listen %s got %s", expected, listen) } diff --git a/monitor_handler_racing_test.go b/monitor_handler_racing_test.go index 1b5bd822c..8b48437db 100644 --- a/monitor_handler_racing_test.go +++ b/monitor_handler_racing_test.go @@ -69,7 +69,7 @@ func getLocalX5U(ag *autographer, x5u string, t *testing.T) (body []byte, err er } func TestMonitorPass(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, conf, _ := newTestAutographer(t) mo := newMonitor(ag, conf.MonitorInterval) var empty []byte @@ -159,7 +159,7 @@ func TestMonitorPass(t *testing.T) { } func TestMonitorHasSignerParameters(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, conf, _ := newTestAutographer(t) mo := newMonitor(ag, conf.MonitorInterval) var empty []byte diff --git a/monitor_handler_test.go b/monitor_handler_test.go index 156b6c4b0..76502d4e0 100644 --- a/monitor_handler_test.go +++ b/monitor_handler_test.go @@ -15,7 +15,7 @@ import ( func TestMonitorNoConfig(t *testing.T) { tmpag := newAutographer(1) - var nomonitor configuration + var nomonitor serviceConfig err := tmpag.addMonitoring(nomonitor.Monitoring) if err != nil { t.Fatalf("adding monitoring authorization failed: %v", err) @@ -28,7 +28,7 @@ func TestMonitorNoConfig(t *testing.T) { func TestMonitorAddDuplicate(t *testing.T) { tmpag := newAutographer(1) - var monitorconf configuration + var monitorconf serviceConfig monitorconf.Monitoring.Key = "xxxxxxx" defer func() { @@ -44,8 +44,8 @@ func TestMonitorAddDuplicate(t *testing.T) { } func TestMonitorBadRequest(t *testing.T) { - ag, conf := newTestAutographer(t) - mo := newMonitor(ag, conf.MonitorInterval) + ag, serviceConf, signerConf := newTestAutographer(t) + mo := newMonitor(ag, serviceConf.MonitorInterval) var TESTCASES = []struct { user string @@ -60,11 +60,11 @@ func TestMonitorBadRequest(t *testing.T) { {``, ``, `/__monitor__`, `HEAD`, ``}, {``, ``, `/__monitor__`, `DELETE`, ``}, // shouldn't have a request body - {`monitor`, conf.Monitoring.Key, `/__monitor__`, `GET`, `[{"input":"y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d"}]`}, + {`monitor`, serviceConf.Monitoring.Key, `/__monitor__`, `GET`, `[{"input":"y0hdfsN8tHlCG82JLywb4d2U+VGWWry8dzwIC3Hk6j32mryUHxUel9SWM5TWkk0d"}]`}, // should use the monitor user - {conf.Authorizations[0].ID, conf.Authorizations[0].Key, `/__monitor__`, `GET`, ``}, + {signerConf.Authorizations[0].ID, signerConf.Authorizations[0].Key, `/__monitor__`, `GET`, ``}, // should use the monitoring key - {`monitor`, conf.Authorizations[0].Key, `/__monitor__`, `GET`, ``}, + {`monitor`, signerConf.Authorizations[0].Key, `/__monitor__`, `GET`, ``}, } for i, testcase := range TESTCASES { body := strings.NewReader(testcase.body) @@ -84,7 +84,7 @@ func TestMonitorBadRequest(t *testing.T) { } func TestMonitorReturnsAllFailures(t *testing.T) { - ag, conf := newTestAutographer(t) + ag, serviceConf, _ := newTestAutographer(t) testPrivateKey := `-----BEGIN PGP PRIVATE KEY BLOCK----- lQHYBGbt/oUBBAC8q+ta2NFHKd5u63Atnpa9g8uU4lXyRkUeROqdS09fDEgNuip9 @@ -138,7 +138,7 @@ eNGDpX35+pcEygI= if err != nil { t.Fatalf("adding signers failed: %v", err) } - mo := newMonitor(ag, conf.MonitorInterval) + mo := newMonitor(ag, serviceConf.MonitorInterval) var empty []byte req, err := http.NewRequest("GET", "http://foo.bar/__monitor__", bytes.NewReader(empty)) @@ -146,7 +146,7 @@ eNGDpX35+pcEygI= t.Fatal(err) } req.Header.Set("Content-Type", "application/json") - authheader := getAuthHeader(req, monitorAuthID, conf.Monitoring.Key, + authheader := getAuthHeader(req, monitorAuthID, serviceConf.Monitoring.Key, sha256.New, id(), "application/json", empty) req.Header.Set("Authorization", authheader) w := httptest.NewRecorder() diff --git a/signer/apk2/README.md b/signer/apk2/README.md index 83adb7c2d..c441d6be0 100644 --- a/signer/apk2/README.md +++ b/signer/apk2/README.md @@ -39,7 +39,7 @@ keytool -exportcert -keystore testkeystore.jks -alias testapp|openssl x509 -info ``` You can then place the certificate and private key in -\`autograph.yaml\`: +\`autograph-signer.yaml\`: ``` yaml signers: diff --git a/signer/contentsignature/README.md b/signer/contentsignature/README.md index 1494214d9..426a685c1 100644 --- a/signer/contentsignature/README.md +++ b/signer/contentsignature/README.md @@ -144,7 +144,7 @@ $ openssl x509 -req -days 365 -in /tmp/autograph-dev.csr -signkey /tmp/autograph Store the CRT on `http://example.net/certs/autograph-dev.crt` and set the x5u -value in `autograph.yaml`. +value in `autograph-signer.yaml`. ``` yaml signers: diff --git a/signer/gpg2/README.md b/signer/gpg2/README.md index 5c08a5d92..e4dce25dc 100644 --- a/signer/gpg2/README.md +++ b/signer/gpg2/README.md @@ -30,7 +30,7 @@ $ gpg --no-default-keyring --keyring /tmp/testkeyring.pgp \ ## Configuration -Add a signer to `autograph.yaml` with the following +Add a signer to `autograph-signer.yaml` with the following **required** fields 1. a PGP public key (e.g. a key exported with `gpg --armor --export diff --git a/signer/gpg2/gpg2_test.go b/signer/gpg2/gpg2_test.go index d79440e7b..7cbd340ff 100644 --- a/signer/gpg2/gpg2_test.go +++ b/signer/gpg2/gpg2_test.go @@ -680,7 +680,7 @@ func TestGPG2Signer_SignFiles(t *testing.T) { } } -// signer configs from the dev autograph.yaml +// signer configs from the dev autograph-signer.yaml //go:embed "test/fixtures/randompgp.key" var randompgpPrivateKey string diff --git a/tools/config-sanitizer/Makefile b/tools/config-sanitizer/Makefile index 2c15634cc..3d0c47298 100644 --- a/tools/config-sanitizer/Makefile +++ b/tools/config-sanitizer/Makefile @@ -44,7 +44,6 @@ docker-debug: ## run bash in generated container docker run -it --rm $(SCRIPT):latest bash test: ## smoke test the script - $(SCRIPT) < ../../autograph.yaml + $(SCRIPT) < ../../autograph-signer.yaml test bd5d37ce116edf18ee1a3c1d7078bfad == $$(md5sum /app/genpki.out && \ cd /app/src/autograph/tools/configurator && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s normandy \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s normandy \ -p issuerprivkey -v "$(grep 'inter key name' /app/genpki.out | awk '{print $4}')" && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s normandy \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s normandy \ -p issuercert -v "$(grep 'inter cert path' /app/genpki.out | awk '{print $4}')" && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s normandy \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s normandy \ -p cacert -v "$(grep 'root cert path' /app/genpki.out | awk '{print $4}')" && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s kinto \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s kinto \ -p issuerprivkey -v "$(grep 'inter key name' /app/genpki.out | awk '{print $4}')" && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s kinto \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s kinto \ -p issuercert -v "$(grep 'inter cert path' /app/genpki.out | awk '{print $4}')" && \ - python3 configurator.py -c /app/autograph.softhsm.yaml -i -s kinto \ + python3 configurator.py -c /app/autograph.softhsm-signer.yaml -i -s kinto \ -p cacert -v "$(grep 'root cert path' /app/genpki.out | awk '{print $4}')" -CMD /go/bin/autograph -c /app/autograph.softhsm.yaml +CMD /go/bin/autograph -c /app/autograph-service.yaml -c /app/autograph.softhsm-service.yaml -s /app/autograph.softhsm-signer.yaml diff --git a/tools/softhsm/autograph.softhsm-service.yaml b/tools/softhsm/autograph.softhsm-service.yaml new file mode 100644 index 000000000..823c12199 --- /dev/null +++ b/tools/softhsm/autograph.softhsm-service.yaml @@ -0,0 +1,42 @@ +server: + listen: "0.0.0.0:8001" + # cache 500k nonces to protect from authorization replay attacks + noncecachesize: 524288 + idletimeout: 60s + readtimeout: 60s + writetimeout: 60s + +# SoftHSM test configuration +hsm: + # this is the location of the softhsm lib on ubuntu xenial, + # it will likely be different on each distribution + path: /usr/lib/softhsm/libsofthsm2.so + tokenlabel: test + pin: "0000" + +# When the DB or HSM is enabled use these timeouts for the +# /__heartbeat__ handler +heartbeat: + hsmchecktimeout: 100ms + dbchecktimeout: 150ms + +# allow hawk authorization headers to be valid for up to 10 minutes +# to account for diverging clocks between client and server +hawktimestampvalidity: 10m + +monitorinterval: 5m + +# When using a postgres database, set the config below: +database: + name: autograph + user: myautographdbuser + password: myautographdbpassword + host: db:5432 + sslmode: verify-full + sslrootcert: /opt/db-root.crt + maxopenconns: 100 + maxidleconns: 10 + monitorpollinterval: 10s + +monitoring: + key: 19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs diff --git a/tools/softhsm/autograph.softhsm.yaml b/tools/softhsm/autograph.softhsm-signer.yaml similarity index 91% rename from tools/softhsm/autograph.softhsm.yaml rename to tools/softhsm/autograph.softhsm-signer.yaml index d7b0a7a52..5f9e086c2 100644 --- a/tools/softhsm/autograph.softhsm.yaml +++ b/tools/softhsm/autograph.softhsm-signer.yaml @@ -1,43 +1,3 @@ -server: - listen: "0.0.0.0:8001" - # cache 500k nonces to protect from authorization replay attacks - noncecachesize: 524288 - idletimeout: 60s - readtimeout: 60s - writetimeout: 60s - -# SoftHSM test configuration -hsm: - # this is the location of the softhsm lib on ubuntu xenial, - # it will likely be different on each distribution - path: /usr/lib/softhsm/libsofthsm2.so - tokenlabel: test - pin: "0000" - -# When the DB or HSM is enabled use these timeouts for the -# /__heartbeat__ handler -heartbeat: - hsmchecktimeout: 100ms - dbchecktimeout: 150ms - -# allow hawk authorization headers to be valid for up to 10 minutes -# to account for diverging clocks between client and server -hawktimestampvalidity: 10m - -monitorinterval: 5m - -# When using a postgres database, set the config below: -database: - name: autograph - user: myautographdbuser - password: myautographdbpassword - host: db:5432 - sslmode: verify-full - sslrootcert: /opt/db-root.crt - maxopenconns: 100 - maxidleconns: 10 - monitorpollinterval: 10s - # The keys below are testing keys that do not grant any power # # NB: the softhsm Dockerfile generates and overwrites certs and keys @@ -271,6 +231,3 @@ authorizations: - hsm-extensions-ecdsa - normandy - hsm-extensions-ecdsa-expired-chain - -monitoring: - key: 19zd4w3xirb5syjgdx8atq6g91m03bdsmzjifs2oddivswlu9qs