[Vendor] update certmagic (#15590)
* update github.com/caddyserver/certmagic v0.12.0 -> v0.13.0 * migrate
This commit is contained in:
parent
e7fc078891
commit
8ea1d32bea
|
@ -32,7 +32,7 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
|
||||||
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
|
||||||
})
|
})
|
||||||
|
|
||||||
magic.Issuer = myACME
|
magic.Issuers = []certmagic.Issuer{myACME}
|
||||||
|
|
||||||
// this obtains certificates or renews them if necessary
|
// this obtains certificates or renews them if necessary
|
||||||
err := magic.ManageSync([]string{domain})
|
err := magic.ManageSync([]string{domain})
|
||||||
|
|
14
go.mod
14
go.mod
|
@ -21,7 +21,7 @@ require (
|
||||||
github.com/blevesearch/bleve/v2 v2.0.2
|
github.com/blevesearch/bleve/v2 v2.0.2
|
||||||
github.com/boombuler/barcode v1.0.1 // indirect
|
github.com/boombuler/barcode v1.0.1 // indirect
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||||
github.com/caddyserver/certmagic v0.12.0
|
github.com/caddyserver/certmagic v0.13.0
|
||||||
github.com/chi-middleware/proxy v1.1.1
|
github.com/chi-middleware/proxy v1.1.1
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||||
|
@ -75,7 +75,6 @@ require (
|
||||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||||
github.com/lafriks/xormstore v1.4.0
|
github.com/lafriks/xormstore v1.4.0
|
||||||
github.com/lib/pq v1.9.0
|
github.com/lib/pq v1.9.0
|
||||||
github.com/libdns/libdns v0.2.0 // indirect
|
|
||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/markbates/goth v1.67.1
|
github.com/markbates/goth v1.67.1
|
||||||
|
@ -84,10 +83,9 @@ require (
|
||||||
github.com/mattn/go-sqlite3 v1.14.6
|
github.com/mattn/go-sqlite3 v1.14.6
|
||||||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81
|
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81
|
||||||
github.com/mgechev/revive v1.0.3
|
github.com/mgechev/revive v1.0.3
|
||||||
github.com/mholt/acmez v0.1.3 // indirect
|
|
||||||
github.com/mholt/archiver/v3 v3.5.0
|
github.com/mholt/archiver/v3 v3.5.0
|
||||||
github.com/microcosm-cc/bluemonday v1.0.7
|
github.com/microcosm-cc/bluemonday v1.0.7
|
||||||
github.com/miekg/dns v1.1.40 // indirect
|
github.com/miekg/dns v1.1.41 // indirect
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/minio-go/v7 v7.0.10
|
github.com/minio/minio-go/v7 v7.0.10
|
||||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||||
|
@ -135,11 +133,11 @@ require (
|
||||||
go.jolheiser.com/pwn v0.0.3
|
go.jolheiser.com/pwn v0.0.3
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
go.uber.org/zap v1.16.0 // indirect
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
|
golang.org/x/net v0.0.0-20210421230115-4e50805a0758
|
||||||
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93
|
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44
|
golang.org/x/sys v0.0.0-20210421221651-33663a62ff08
|
||||||
golang.org/x/text v0.3.5
|
golang.org/x/text v0.3.6
|
||||||
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
|
||||||
golang.org/x/tools v0.1.0
|
golang.org/x/tools v0.1.0
|
||||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||||
|
|
32
go.sum
32
go.sum
|
@ -185,8 +185,8 @@ github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||||
github.com/caddyserver/certmagic v0.12.0 h1:1f7kxykaJkOVVpXJ8ZrC6RAO5F6+kKm9U7dBFbLNeug=
|
github.com/caddyserver/certmagic v0.13.0 h1:ky0rntZvIFiUKFdIikYxj31WN+Ts0Od6Wjz83iTzxfc=
|
||||||
github.com/caddyserver/certmagic v0.12.0/go.mod h1:tr26xh+9fY5dN0J6IPAlMj07qpog22PJKa7Nw7j835U=
|
github.com/caddyserver/certmagic v0.13.0/go.mod h1:dNOzF4iOB7H9E51xTooMB90vs+2XNVtpnx0liQNsQY4=
|
||||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
|
@ -769,7 +769,6 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
|
||||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
|
||||||
github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI=
|
github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI=
|
||||||
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||||
|
@ -825,7 +824,6 @@ github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81 h1:QASJXOGm2RZ5Ardbc8
|
||||||
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81/go.mod h1:KQ7+USdGKfpPjXk4Ga+5XxQM4Lm4e3gAogrreFAYpOg=
|
||||||
github.com/mgechev/revive v1.0.3 h1:z3FL6IFFN3JKzHYHD8O1ExH9g/4lAGJ5x1+9rPZgsFg=
|
github.com/mgechev/revive v1.0.3 h1:z3FL6IFFN3JKzHYHD8O1ExH9g/4lAGJ5x1+9rPZgsFg=
|
||||||
github.com/mgechev/revive v1.0.3/go.mod h1:POGGZagSo/0frdr7VeAifzS5Uka0d0GPiM35MsTO8nE=
|
github.com/mgechev/revive v1.0.3/go.mod h1:POGGZagSo/0frdr7VeAifzS5Uka0d0GPiM35MsTO8nE=
|
||||||
github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
|
||||||
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
|
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
|
||||||
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
||||||
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
|
github.com/mholt/archiver/v3 v3.5.0 h1:nE8gZIrw66cu4osS/U7UW7YDuGMHssxKutU8IfWxwWE=
|
||||||
|
@ -834,8 +832,8 @@ github.com/microcosm-cc/bluemonday v1.0.7 h1:6yAQfk4XT+PI/dk1ZeBp1gr3Q2Hd1DR0O3a
|
||||||
github.com/microcosm-cc/bluemonday v1.0.7/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=
|
github.com/microcosm-cc/bluemonday v1.0.7/go.mod h1:HOT/6NaBlR0f9XlxD3zolN6Z3N8Lp4pvhp+jLS5ihnI=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
|
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
|
||||||
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||||
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
github.com/minio/md5-simd v1.1.0/go.mod h1:XpBqgZULrMYD3R+M28PcmP0CkI7PEMzB3U77ZrKZ0Gw=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
|
@ -1113,8 +1111,6 @@ github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44 h1:7bSo/vjZKVYUoZfxpY
|
||||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
|
||||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs=
|
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae h1:ihaXiJkaca54IaCSnEXtE/uSZOmPxKZhDfVLrzZLFDs=
|
||||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM=
|
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae/go.mod h1:1fdkY6xxl6ExVs2QFv7R0F5IRZHKA8RahhB9fMC9RvM=
|
||||||
github.com/unrolled/render v1.0.3 h1:baO+NG1bZSF2WR4zwh+0bMWauWky7DVrTOfvE2w+aFo=
|
|
||||||
github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
|
|
||||||
github.com/unrolled/render v1.1.0 h1:gvpR9hHxTt6DcGqRYuVVFcfd8rtK+nyEPUJN06KB57Q=
|
github.com/unrolled/render v1.1.0 h1:gvpR9hHxTt6DcGqRYuVVFcfd8rtK+nyEPUJN06KB57Q=
|
||||||
github.com/unrolled/render v1.1.0/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
|
github.com/unrolled/render v1.1.0/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
|
@ -1231,8 +1227,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
||||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -1321,9 +1317,10 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
golang.org/x/net v0.0.0-20210421230115-4e50805a0758 h1:aEpZnXcAmXkd6AvLb2OPt+EN1Zu/8Ne3pCqPjja5PXY=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -1346,8 +1343,9 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -1419,8 +1417,11 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210421221651-33663a62ff08 h1:qyN5bV+96OX8pL78eXDuz6YlDPzCYgdW74H5yE9BoSU=
|
||||||
|
golang.org/x/sys v0.0.0-20210421221651-33663a62ff08/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
@ -1430,8 +1431,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
|
||||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
|
|
@ -260,7 +260,7 @@ magic := certmagic.New(cache, certmagic.Config{
|
||||||
// any customizations you need go here
|
// any customizations you need go here
|
||||||
})
|
})
|
||||||
|
|
||||||
myACME := certmagic.NewACMEManager(magic, ACMEManager{
|
myACME := certmagic.NewACMEManager(magic, certmagic.ACMEManager{
|
||||||
CA: certmagic.LetsEncryptStagingCA,
|
CA: certmagic.LetsEncryptStagingCA,
|
||||||
Email: "you@yours.com",
|
Email: "you@yours.com",
|
||||||
Agreed: true,
|
Agreed: true,
|
||||||
|
@ -285,7 +285,7 @@ tlsConfig := magic.TLSConfig()
|
||||||
// we can simply set its GetCertificate field and append the
|
// we can simply set its GetCertificate field and append the
|
||||||
// TLS-ALPN challenge protocol to the NextProtos
|
// TLS-ALPN challenge protocol to the NextProtos
|
||||||
myTLSConfig.GetCertificate = magic.GetCertificate
|
myTLSConfig.GetCertificate = magic.GetCertificate
|
||||||
myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol}
|
myTLSConfig.NextProtos = append(myTLSConfig.NextProtos, tlsalpn01.ACMETLS1Protocol)
|
||||||
|
|
||||||
// the HTTP challenge has to be handled by your HTTP server;
|
// the HTTP challenge has to be handled by your HTTP server;
|
||||||
// if you don't have one, you should have disabled it earlier
|
// if you don't have one, you should have disabled it earlier
|
||||||
|
@ -394,7 +394,7 @@ To enable it, just set the `DNS01Solver` field on a `certmagic.ACMEManager` stru
|
||||||
import "github.com/libdns/cloudflare"
|
import "github.com/libdns/cloudflare"
|
||||||
|
|
||||||
certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{
|
certmagic.DefaultACME.DNS01Solver = &certmagic.DNS01Solver{
|
||||||
DNSProvider: cloudflare.Provider{
|
DNSProvider: &cloudflare.Provider{
|
||||||
APIToken: "topsecret",
|
APIToken: "topsecret",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ package certmagic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
@ -33,18 +35,24 @@ import (
|
||||||
// getAccount either loads or creates a new account, depending on if
|
// getAccount either loads or creates a new account, depending on if
|
||||||
// an account can be found in storage for the given CA + email combo.
|
// an account can be found in storage for the given CA + email combo.
|
||||||
func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) {
|
func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) {
|
||||||
regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email))
|
acct, err := am.loadAccount(ca, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(ErrNotExist); ok {
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
return am.newAccount(email)
|
return am.newAccount(email)
|
||||||
}
|
}
|
||||||
|
return acct, err
|
||||||
|
}
|
||||||
|
return acct, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadAccount loads an account from storage, but does not create a new one.
|
||||||
|
func (am *ACMEManager) loadAccount(ca, email string) (acme.Account, error) {
|
||||||
|
regBytes, err := am.config.Storage.Load(am.storageKeyUserReg(ca, email))
|
||||||
|
if err != nil {
|
||||||
return acme.Account{}, err
|
return acme.Account{}, err
|
||||||
}
|
}
|
||||||
keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email))
|
keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(ca, email))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(ErrNotExist); ok {
|
|
||||||
return am.newAccount(email)
|
|
||||||
}
|
|
||||||
return acme.Account{}, err
|
return acme.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,54 +66,6 @@ func (am *ACMEManager) getAccount(ca, email string) (acme.Account, error) {
|
||||||
return acct, fmt.Errorf("could not decode account's private key: %v", err)
|
return acct, fmt.Errorf("could not decode account's private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: July 2020 - transition to new ACME lib and account structure;
|
|
||||||
// for a while, we will need to convert old accounts to new structure
|
|
||||||
acct, err = am.transitionAccountToACMEzJuly2020Format(ca, acct, regBytes)
|
|
||||||
if err != nil {
|
|
||||||
return acct, fmt.Errorf("one-time account transition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return acct, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this is a temporary transition helper starting July 2020.
|
|
||||||
// It can go away when we think enough time has passed that most active assets have transitioned.
|
|
||||||
func (am *ACMEManager) transitionAccountToACMEzJuly2020Format(ca string, acct acme.Account, regBytes []byte) (acme.Account, error) {
|
|
||||||
if acct.Status != "" && acct.Location != "" {
|
|
||||||
return acct, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldAcct struct {
|
|
||||||
Email string `json:"Email"`
|
|
||||||
Registration struct {
|
|
||||||
Body struct {
|
|
||||||
Status string `json:"status"`
|
|
||||||
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed"`
|
|
||||||
Orders string `json:"orders"`
|
|
||||||
ExternalAccountBinding json.RawMessage `json:"externalAccountBinding"`
|
|
||||||
} `json:"body"`
|
|
||||||
URI string `json:"uri"`
|
|
||||||
} `json:"Registration"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(regBytes, &oldAcct)
|
|
||||||
if err != nil {
|
|
||||||
return acct, fmt.Errorf("decoding into old account type: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
acct.Status = oldAcct.Registration.Body.Status
|
|
||||||
acct.TermsOfServiceAgreed = oldAcct.Registration.Body.TermsOfServiceAgreed
|
|
||||||
acct.Location = oldAcct.Registration.URI
|
|
||||||
acct.ExternalAccountBinding = oldAcct.Registration.Body.ExternalAccountBinding
|
|
||||||
acct.Orders = oldAcct.Registration.Body.Orders
|
|
||||||
if oldAcct.Email != "" {
|
|
||||||
acct.Contact = []string{"mailto:" + oldAcct.Email}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = am.saveAccount(ca, acct)
|
|
||||||
if err != nil {
|
|
||||||
return acct, fmt.Errorf("saving converted account: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return acct, nil
|
return acct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +84,71 @@ func (*ACMEManager) newAccount(email string) (acme.Account, error) {
|
||||||
return acct, nil
|
return acct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAccount first tries loading the account with the associated private key from storage.
|
||||||
|
// If it does not exist in storage, it will be retrieved from the ACME server and added to storage.
|
||||||
|
// The account must already exist; it does not create a new account.
|
||||||
|
func (am *ACMEManager) GetAccount(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) {
|
||||||
|
account, err := am.loadAccountByKey(ctx, privateKeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
|
account, err = am.lookUpAccount(ctx, privateKeyPEM)
|
||||||
|
} else {
|
||||||
|
return account, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return account, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadAccountByKey loads the account with the given private key from storage, if it exists.
|
||||||
|
// If it does not exist, an error of type ErrNotExist is returned. This is not very efficient
|
||||||
|
// for lots of accounts.
|
||||||
|
func (am *ACMEManager) loadAccountByKey(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) {
|
||||||
|
accountList, err := am.config.Storage.List(am.storageKeyUsersPrefix(am.CA), false)
|
||||||
|
if err != nil {
|
||||||
|
return acme.Account{}, err
|
||||||
|
}
|
||||||
|
for _, accountFolderKey := range accountList {
|
||||||
|
email := path.Base(accountFolderKey)
|
||||||
|
keyBytes, err := am.config.Storage.Load(am.storageKeyUserPrivateKey(am.CA, email))
|
||||||
|
if err != nil {
|
||||||
|
return acme.Account{}, err
|
||||||
|
}
|
||||||
|
if bytes.Equal(bytes.TrimSpace(keyBytes), bytes.TrimSpace(privateKeyPEM)) {
|
||||||
|
return am.loadAccount(am.CA, email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acme.Account{}, ErrNotExist(fmt.Errorf("no account found with that key"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// lookUpAccount looks up the account associated with privateKeyPEM from the ACME server.
|
||||||
|
// If the account is found by the server, it will be saved to storage and returned.
|
||||||
|
func (am *ACMEManager) lookUpAccount(ctx context.Context, privateKeyPEM []byte) (acme.Account, error) {
|
||||||
|
client, err := am.newACMEClient(false)
|
||||||
|
if err != nil {
|
||||||
|
return acme.Account{}, fmt.Errorf("creating ACME client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKey, err := decodePrivateKey([]byte(privateKeyPEM))
|
||||||
|
if err != nil {
|
||||||
|
return acme.Account{}, fmt.Errorf("decoding private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up the account
|
||||||
|
account := acme.Account{PrivateKey: privateKey}
|
||||||
|
account, err = client.GetAccount(ctx, account)
|
||||||
|
if err != nil {
|
||||||
|
return acme.Account{}, fmt.Errorf("looking up account with server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the account details to storage
|
||||||
|
err = am.saveAccount(client.Directory, account)
|
||||||
|
if err != nil {
|
||||||
|
return account, fmt.Errorf("could not save account to storage: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
||||||
// saveAccount persists an ACME account's info and private key to storage.
|
// saveAccount persists an ACME account's info and private key to storage.
|
||||||
// It does NOT register the account via ACME or prompt the user.
|
// It does NOT register the account via ACME or prompt the user.
|
||||||
func (am *ACMEManager) saveAccount(ca string, account acme.Account) error {
|
func (am *ACMEManager) saveAccount(ca string, account acme.Account) error {
|
||||||
|
@ -242,8 +267,12 @@ func (am *ACMEManager) askUserAgreement(agreementURL string) bool {
|
||||||
return answer == "y" || answer == "yes"
|
return answer == "y" || answer == "yes"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func storageKeyACMECAPrefix(issuerKey string) string {
|
||||||
|
return path.Join(prefixACME, StorageKeys.Safe(issuerKey))
|
||||||
|
}
|
||||||
|
|
||||||
func (am *ACMEManager) storageKeyCAPrefix(caURL string) string {
|
func (am *ACMEManager) storageKeyCAPrefix(caURL string) string {
|
||||||
return path.Join(prefixACME, StorageKeys.Safe(am.issuerKey(caURL)))
|
return storageKeyACMECAPrefix(am.issuerKey(caURL))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *ACMEManager) storageKeyUsersPrefix(caURL string) string {
|
func (am *ACMEManager) storageKeyUsersPrefix(caURL string) string {
|
||||||
|
@ -305,7 +334,8 @@ func (am *ACMEManager) mostRecentAccountEmail(caURL string) (string, bool) {
|
||||||
// get all the key infos ahead of sorting, because
|
// get all the key infos ahead of sorting, because
|
||||||
// we might filter some out
|
// we might filter some out
|
||||||
stats := make(map[string]KeyInfo)
|
stats := make(map[string]KeyInfo)
|
||||||
for i, u := range accountList {
|
for i := 0; i < len(accountList); i++ {
|
||||||
|
u := accountList[i]
|
||||||
keyInfo, err := am.config.Storage.Stat(u)
|
keyInfo, err := am.config.Storage.Stat(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -318,6 +348,7 @@ func (am *ACMEManager) mostRecentAccountEmail(caURL string) (string, bool) {
|
||||||
// frankly one's OS shouldn't mess with the data folder
|
// frankly one's OS shouldn't mess with the data folder
|
||||||
// in the first place.
|
// in the first place.
|
||||||
accountList = append(accountList[:i], accountList[i+1:]...)
|
accountList = append(accountList[:i], accountList[i+1:]...)
|
||||||
|
i--
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
stats[u] = keyInfo
|
stats[u] = keyInfo
|
||||||
|
|
|
@ -37,19 +37,104 @@ func init() {
|
||||||
weakrand.Seed(time.Now().UnixNano())
|
weakrand.Seed(time.Now().UnixNano())
|
||||||
}
|
}
|
||||||
|
|
||||||
// acmeClient holds state necessary for us to perform
|
// acmeClient holds state necessary to perform ACME operations
|
||||||
// ACME operations for certificate management. Call
|
// for certificate management with an ACME account. Call
|
||||||
// ACMEManager.newACMEClient() to get a valid one to .
|
// ACMEManager.newACMEClientWithAccount() to get a valid one.
|
||||||
type acmeClient struct {
|
type acmeClient struct {
|
||||||
mgr *ACMEManager
|
mgr *ACMEManager
|
||||||
acmeClient *acmez.Client
|
acmeClient *acmez.Client
|
||||||
account acme.Account
|
account acme.Account
|
||||||
}
|
}
|
||||||
|
|
||||||
// newACMEClient creates the underlying ACME library client type.
|
// newACMEClientWithAccount creates an ACME client ready to use with an account, including
|
||||||
// If useTestCA is true, am.TestCA will be used if it is set;
|
// loading one from storage or registering a new account with the CA if necessary. If
|
||||||
// otherwise, the primary CA will still be used.
|
// useTestCA is true, am.TestCA will be used if set; otherwise, the primary CA will be used.
|
||||||
func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) {
|
func (am *ACMEManager) newACMEClientWithAccount(ctx context.Context, useTestCA, interactive bool) (*acmeClient, error) {
|
||||||
|
// first, get underlying ACME client
|
||||||
|
client, err := am.newACMEClient(useTestCA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up or create the ACME account
|
||||||
|
var account acme.Account
|
||||||
|
if am.AccountKeyPEM != "" {
|
||||||
|
account, err = am.GetAccount(ctx, []byte(am.AccountKeyPEM))
|
||||||
|
} else {
|
||||||
|
account, err = am.getAccount(client.Directory, am.Email)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting ACME account: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// register account if it is new
|
||||||
|
if account.Status == "" {
|
||||||
|
if am.NewAccountFunc != nil {
|
||||||
|
account, err = am.NewAccountFunc(ctx, am, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("account pre-registration callback: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// agree to terms
|
||||||
|
if interactive {
|
||||||
|
if !am.Agreed {
|
||||||
|
var termsURL string
|
||||||
|
dir, err := client.GetDirectory(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting directory: %w", err)
|
||||||
|
}
|
||||||
|
if dir.Meta != nil {
|
||||||
|
termsURL = dir.Meta.TermsOfService
|
||||||
|
}
|
||||||
|
if termsURL != "" {
|
||||||
|
am.Agreed = am.askUserAgreement(termsURL)
|
||||||
|
if !am.Agreed {
|
||||||
|
return nil, fmt.Errorf("user must agree to CA terms")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// can't prompt a user who isn't there; they should
|
||||||
|
// have reviewed the terms beforehand
|
||||||
|
am.Agreed = true
|
||||||
|
}
|
||||||
|
account.TermsOfServiceAgreed = am.Agreed
|
||||||
|
|
||||||
|
// associate account with external binding, if configured
|
||||||
|
if am.ExternalAccount != nil {
|
||||||
|
err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create account
|
||||||
|
account, err = client.NewAccount(ctx, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("registering account %v with server: %w", account.Contact, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// persist the account to storage
|
||||||
|
err = am.saveAccount(client.Directory, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not save account %v: %v", account.Contact, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c := &acmeClient{
|
||||||
|
mgr: am,
|
||||||
|
acmeClient: client,
|
||||||
|
account: account,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newACMEClient creates a new underlying ACME client using the settings in am,
|
||||||
|
// independent of any particular ACME account. If useTestCA is true, am.TestCA
|
||||||
|
// will be used if it is set; otherwise, the primary CA will be used.
|
||||||
|
func (am *ACMEManager) newACMEClient(useTestCA bool) (*acmez.Client, error) {
|
||||||
// ensure defaults are filled in
|
// ensure defaults are filled in
|
||||||
var caURL string
|
var caURL string
|
||||||
if useTestCA {
|
if useTestCA {
|
||||||
|
@ -78,12 +163,6 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive
|
||||||
return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL)
|
return nil, fmt.Errorf("%s: insecure CA URL (HTTPS required)", caURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// look up or create the ACME account
|
|
||||||
account, err := am.getAccount(caURL, am.Email)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("getting ACME account: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up the dialers and resolver for the ACME client's HTTP client
|
// set up the dialers and resolver for the ACME client's HTTP client
|
||||||
dialer := &net.Dialer{
|
dialer := &net.Dialer{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
|
@ -153,12 +232,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive
|
||||||
useHTTPPort = am.AltHTTPPort
|
useHTTPPort = am.AltHTTPPort
|
||||||
}
|
}
|
||||||
client.ChallengeSolvers[acme.ChallengeTypeHTTP01] = distributedSolver{
|
client.ChallengeSolvers[acme.ChallengeTypeHTTP01] = distributedSolver{
|
||||||
acmeManager: am,
|
storage: am.config.Storage,
|
||||||
|
storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory),
|
||||||
solver: &httpSolver{
|
solver: &httpSolver{
|
||||||
acmeManager: am,
|
acmeManager: am,
|
||||||
address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useHTTPPort)),
|
address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useHTTPPort)),
|
||||||
},
|
},
|
||||||
caURL: client.Directory,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,12 +251,12 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive
|
||||||
useTLSALPNPort = am.AltTLSALPNPort
|
useTLSALPNPort = am.AltTLSALPNPort
|
||||||
}
|
}
|
||||||
client.ChallengeSolvers[acme.ChallengeTypeTLSALPN01] = distributedSolver{
|
client.ChallengeSolvers[acme.ChallengeTypeTLSALPN01] = distributedSolver{
|
||||||
acmeManager: am,
|
storage: am.config.Storage,
|
||||||
|
storageKeyIssuerPrefix: am.storageKeyCAPrefix(client.Directory),
|
||||||
solver: &tlsALPNSolver{
|
solver: &tlsALPNSolver{
|
||||||
config: am.config,
|
config: am.config,
|
||||||
address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useTLSALPNPort)),
|
address: net.JoinHostPort(am.ListenHost, strconv.Itoa(useTLSALPNPort)),
|
||||||
},
|
},
|
||||||
caURL: client.Directory,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -185,68 +264,26 @@ func (am *ACMEManager) newACMEClient(ctx context.Context, useTestCA, interactive
|
||||||
client.ChallengeSolvers[acme.ChallengeTypeDNS01] = am.DNS01Solver
|
client.ChallengeSolvers[acme.ChallengeTypeDNS01] = am.DNS01Solver
|
||||||
}
|
}
|
||||||
|
|
||||||
// register account if it is new
|
// wrap solvers in our wrapper so that we can keep track of challenge
|
||||||
if account.Status == "" {
|
// info: this is useful for solving challenges globally as a process;
|
||||||
if am.NewAccountFunc != nil {
|
// for example, usually there is only one process that can solve the
|
||||||
err = am.NewAccountFunc(ctx, am, account)
|
// HTTP and TLS-ALPN challenges, and only one server in that process
|
||||||
if err != nil {
|
// that can bind the necessary port(s), so if a server listening on
|
||||||
return nil, fmt.Errorf("account pre-registration callback: %v", err)
|
// a different port needed a certificate, it would have to know about
|
||||||
}
|
// the other server listening on that port, and somehow convey its
|
||||||
}
|
// challenge info or share its config, but this isn't always feasible;
|
||||||
|
// what the wrapper does is it accesses a global challenge memory so
|
||||||
// agree to terms
|
// that unrelated servers in this process can all solve each others'
|
||||||
if interactive {
|
// challenges without having to know about each other - Caddy's admin
|
||||||
if !am.Agreed {
|
// endpoint uses this functionality since it and the HTTP/TLS modules
|
||||||
var termsURL string
|
// do not know about each other
|
||||||
dir, err := client.GetDirectory(ctx)
|
// (doing this here in a separate loop ensures that even if we expose
|
||||||
if err != nil {
|
// solver config to users later, we will even wrap their own solvers)
|
||||||
return nil, fmt.Errorf("getting directory: %w", err)
|
for name, solver := range client.ChallengeSolvers {
|
||||||
}
|
client.ChallengeSolvers[name] = solverWrapper{solver}
|
||||||
if dir.Meta != nil {
|
|
||||||
termsURL = dir.Meta.TermsOfService
|
|
||||||
}
|
|
||||||
if termsURL != "" {
|
|
||||||
am.Agreed = am.askUserAgreement(termsURL)
|
|
||||||
if !am.Agreed {
|
|
||||||
return nil, fmt.Errorf("user must agree to CA terms")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// can't prompt a user who isn't there; they should
|
|
||||||
// have reviewed the terms beforehand
|
|
||||||
am.Agreed = true
|
|
||||||
}
|
|
||||||
account.TermsOfServiceAgreed = am.Agreed
|
|
||||||
|
|
||||||
// associate account with external binding, if configured
|
|
||||||
if am.ExternalAccount != nil {
|
|
||||||
err := account.SetExternalAccountBinding(ctx, client.Client, *am.ExternalAccount)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create account
|
|
||||||
account, err = client.NewAccount(ctx, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("registering account with server: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// persist the account to storage
|
|
||||||
err = am.saveAccount(caURL, account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not save account: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &acmeClient{
|
return client, nil
|
||||||
mgr: am,
|
|
||||||
acmeClient: client,
|
|
||||||
account: account,
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *acmeClient) throttle(ctx context.Context, names []string) error {
|
func (c *acmeClient) throttle(ctx context.Context, names []string) error {
|
||||||
|
@ -325,7 +362,7 @@ var (
|
||||||
|
|
||||||
// RateLimitEvents is how many new events can be allowed
|
// RateLimitEvents is how many new events can be allowed
|
||||||
// in RateLimitEventsWindow.
|
// in RateLimitEventsWindow.
|
||||||
RateLimitEvents = 10
|
RateLimitEvents = 20
|
||||||
|
|
||||||
// RateLimitEventsWindow is the size of the sliding
|
// RateLimitEventsWindow is the size of the sliding
|
||||||
// window that throttles events.
|
// window that throttles events.
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ import (
|
||||||
// Issuer, and Revoker interfaces.
|
// Issuer, and Revoker interfaces.
|
||||||
//
|
//
|
||||||
// It is NOT VALID to use an ACMEManager without calling NewACMEManager().
|
// It is NOT VALID to use an ACMEManager without calling NewACMEManager().
|
||||||
// It fills in default values from DefaultACME as well as setting up
|
// It fills in any default values from DefaultACME as well as setting up
|
||||||
// internal state that is necessary for valid use. Always call
|
// internal state that is necessary for valid use. Always call
|
||||||
// NewACMEManager() to get a valid ACMEManager value.
|
// NewACMEManager() to get a valid ACMEManager value.
|
||||||
type ACMEManager struct {
|
type ACMEManager struct {
|
||||||
|
@ -37,6 +38,12 @@ type ACMEManager struct {
|
||||||
// selecting an existing ACME server account
|
// selecting an existing ACME server account
|
||||||
Email string
|
Email string
|
||||||
|
|
||||||
|
// The PEM-encoded private key of the ACME
|
||||||
|
// account to use; only needed if the account
|
||||||
|
// is already created on the server and
|
||||||
|
// can be looked up with the ACME protocol
|
||||||
|
AccountKeyPEM string
|
||||||
|
|
||||||
// Set to true if agreed to the CA's
|
// Set to true if agreed to the CA's
|
||||||
// subscriber agreement
|
// subscriber agreement
|
||||||
Agreed bool
|
Agreed bool
|
||||||
|
@ -92,9 +99,13 @@ type ACMEManager struct {
|
||||||
// Callback function that is called before a
|
// Callback function that is called before a
|
||||||
// new ACME account is registered with the CA;
|
// new ACME account is registered with the CA;
|
||||||
// it allows for last-second config changes
|
// it allows for last-second config changes
|
||||||
// of the ACMEManager (TODO: this feature is
|
// of the ACMEManager and the Account.
|
||||||
// still EXPERIMENTAL and subject to change)
|
// (TODO: this feature is still EXPERIMENTAL and subject to change)
|
||||||
NewAccountFunc func(context.Context, *ACMEManager, acme.Account) error
|
NewAccountFunc func(context.Context, *ACMEManager, acme.Account) (acme.Account, error)
|
||||||
|
|
||||||
|
// Preferences for selecting alternate
|
||||||
|
// certificate chains
|
||||||
|
PreferredChains ChainPreference
|
||||||
|
|
||||||
// Set a logger to enable logging
|
// Set a logger to enable logging
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
|
@ -105,10 +116,12 @@ type ACMEManager struct {
|
||||||
|
|
||||||
// NewACMEManager constructs a valid ACMEManager based on a template
|
// NewACMEManager constructs a valid ACMEManager based on a template
|
||||||
// configuration; any empty values will be filled in by defaults in
|
// configuration; any empty values will be filled in by defaults in
|
||||||
// DefaultACME. The associated config is also required.
|
// DefaultACME, and if any required values are still empty, sensible
|
||||||
|
// defaults will be used.
|
||||||
//
|
//
|
||||||
// Typically, you'll create the Config first, then call NewACMEManager(),
|
// Typically, you'll create the Config first with New() or NewDefault(),
|
||||||
// then assign the return value to the Issuer/Revoker fields of the Config.
|
// then call NewACMEManager(), then assign the return value to the Issuers
|
||||||
|
// field of the Config.
|
||||||
func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager {
|
func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
panic("cannot make valid ACMEManager without an associated CertMagic config")
|
panic("cannot make valid ACMEManager without an associated CertMagic config")
|
||||||
|
@ -126,6 +139,9 @@ func NewACMEManager(cfg *Config, template ACMEManager) *ACMEManager {
|
||||||
if template.Email == "" {
|
if template.Email == "" {
|
||||||
template.Email = DefaultACME.Email
|
template.Email = DefaultACME.Email
|
||||||
}
|
}
|
||||||
|
if template.AccountKeyPEM == "" {
|
||||||
|
template.AccountKeyPEM = DefaultACME.AccountKeyPEM
|
||||||
|
}
|
||||||
if !template.Agreed {
|
if !template.Agreed {
|
||||||
template.Agreed = DefaultACME.Agreed
|
template.Agreed = DefaultACME.Agreed
|
||||||
}
|
}
|
||||||
|
@ -175,7 +191,7 @@ func (am *ACMEManager) IssuerKey() string {
|
||||||
return am.issuerKey(am.CA)
|
return am.issuerKey(am.CA)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *ACMEManager) issuerKey(ca string) string {
|
func (*ACMEManager) issuerKey(ca string) string {
|
||||||
key := ca
|
key := ca
|
||||||
if caURL, err := url.Parse(key); err == nil {
|
if caURL, err := url.Parse(key); err == nil {
|
||||||
key = caURL.Host
|
key = caURL.Host
|
||||||
|
@ -202,11 +218,11 @@ func (am *ACMEManager) issuerKey(ca string) string {
|
||||||
// batch is eligible for certificates if using Let's Encrypt.
|
// batch is eligible for certificates if using Let's Encrypt.
|
||||||
// It also ensures that an email address is available.
|
// It also ensures that an email address is available.
|
||||||
func (am *ACMEManager) PreCheck(_ context.Context, names []string, interactive bool) error {
|
func (am *ACMEManager) PreCheck(_ context.Context, names []string, interactive bool) error {
|
||||||
letsEncrypt := strings.Contains(am.CA, "api.letsencrypt.org")
|
publicCA := strings.Contains(am.CA, "api.letsencrypt.org") || strings.Contains(am.CA, "acme.zerossl.com")
|
||||||
if letsEncrypt {
|
if publicCA {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if !SubjectQualifiesForPublicCert(name) {
|
if !SubjectQualifiesForPublicCert(name) {
|
||||||
return fmt.Errorf("subject does not qualify for a Let's Encrypt certificate: %s", name)
|
return fmt.Errorf("subject does not qualify for a public certificate: %s", name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,7 +298,7 @@ func (am *ACMEManager) Issue(ctx context.Context, csr *x509.CertificateRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest, useTestCA bool) (*IssuedCertificate, bool, error) {
|
func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest, useTestCA bool) (*IssuedCertificate, bool, error) {
|
||||||
client, err := am.newACMEClient(ctx, useTestCA, false)
|
client, err := am.newACMEClientWithAccount(ctx, useTestCA, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
@ -300,20 +316,103 @@ func (am *ACMEManager) doIssue(ctx context.Context, csr *x509.CertificateRequest
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory)
|
return nil, usingTestCA, fmt.Errorf("%v %w (ca=%s)", nameSet, err, client.acmeClient.Directory)
|
||||||
}
|
}
|
||||||
|
if len(certChains) == 0 {
|
||||||
|
return nil, usingTestCA, fmt.Errorf("no certificate chains")
|
||||||
|
}
|
||||||
|
|
||||||
|
preferredChain := am.selectPreferredChain(certChains)
|
||||||
|
|
||||||
// TODO: ACME server could in theory issue a cert with multiple chains,
|
|
||||||
// but we don't (yet) have a way to choose one, so just use first one
|
|
||||||
ic := &IssuedCertificate{
|
ic := &IssuedCertificate{
|
||||||
Certificate: certChains[0].ChainPEM,
|
Certificate: preferredChain.ChainPEM,
|
||||||
Metadata: certChains[0],
|
Metadata: preferredChain,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ic, usingTestCA, nil
|
return ic, usingTestCA, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// selectPreferredChain sorts and then filters the certificate chains to find the optimal
|
||||||
|
// chain preferred by the client. If there's only one chain, that is returned without any
|
||||||
|
// processing. If there are no matches, the first chain is returned.
|
||||||
|
func (am *ACMEManager) selectPreferredChain(certChains []acme.Certificate) acme.Certificate {
|
||||||
|
if len(certChains) == 1 {
|
||||||
|
if am.Logger != nil && (len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0) {
|
||||||
|
am.Logger.Debug("there is only one chain offered; selecting it regardless of preferences",
|
||||||
|
zap.String("chain_url", certChains[0].URL))
|
||||||
|
}
|
||||||
|
return certChains[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if am.PreferredChains.Smallest != nil {
|
||||||
|
if *am.PreferredChains.Smallest {
|
||||||
|
sort.Slice(certChains, func(i, j int) bool {
|
||||||
|
return len(certChains[i].ChainPEM) < len(certChains[j].ChainPEM)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
sort.Slice(certChains, func(i, j int) bool {
|
||||||
|
return len(certChains[i].ChainPEM) > len(certChains[j].ChainPEM)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(am.PreferredChains.AnyCommonName) > 0 || len(am.PreferredChains.RootCommonName) > 0 {
|
||||||
|
// in order to inspect, we need to decode their PEM contents
|
||||||
|
decodedChains := make([][]*x509.Certificate, len(certChains))
|
||||||
|
for i, chain := range certChains {
|
||||||
|
certs, err := parseCertsFromPEMBundle(chain.ChainPEM)
|
||||||
|
if err != nil {
|
||||||
|
if am.Logger != nil {
|
||||||
|
am.Logger.Error("unable to parse PEM certificate chain",
|
||||||
|
zap.Int("chain", i),
|
||||||
|
zap.Error(err))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
decodedChains[i] = certs
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(am.PreferredChains.AnyCommonName) > 0 {
|
||||||
|
for _, prefAnyCN := range am.PreferredChains.AnyCommonName {
|
||||||
|
for i, chain := range decodedChains {
|
||||||
|
for _, cert := range chain {
|
||||||
|
if cert.Issuer.CommonName == prefAnyCN {
|
||||||
|
if am.Logger != nil {
|
||||||
|
am.Logger.Debug("found preferred certificate chain by issuer common name",
|
||||||
|
zap.String("preference", prefAnyCN),
|
||||||
|
zap.Int("chain", i))
|
||||||
|
}
|
||||||
|
return certChains[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(am.PreferredChains.RootCommonName) > 0 {
|
||||||
|
for _, prefRootCN := range am.PreferredChains.RootCommonName {
|
||||||
|
for i, chain := range decodedChains {
|
||||||
|
if chain[len(chain)-1].Issuer.CommonName == prefRootCN {
|
||||||
|
if am.Logger != nil {
|
||||||
|
am.Logger.Debug("found preferred certificate chain by root common name",
|
||||||
|
zap.String("preference", prefRootCN),
|
||||||
|
zap.Int("chain", i))
|
||||||
|
}
|
||||||
|
return certChains[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if am.Logger != nil {
|
||||||
|
am.Logger.Warn("did not find chain matching preferences; using first")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return certChains[0]
|
||||||
|
}
|
||||||
|
|
||||||
// Revoke implements the Revoker interface. It revokes the given certificate.
|
// Revoke implements the Revoker interface. It revokes the given certificate.
|
||||||
func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, reason int) error {
|
func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, reason int) error {
|
||||||
client, err := am.newACMEClient(ctx, false, false)
|
client, err := am.newACMEClientWithAccount(ctx, false, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -326,8 +425,24 @@ func (am *ACMEManager) Revoke(ctx context.Context, cert CertificateResource, rea
|
||||||
return client.revoke(ctx, certs[0], reason)
|
return client.revoke(ctx, certs[0], reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultACME specifies the default settings
|
// ChainPreference describes the client's preferred certificate chain,
|
||||||
// to use for ACMEManagers.
|
// useful if the CA offers alternate chains. The first matching chain
|
||||||
|
// will be selected.
|
||||||
|
type ChainPreference struct {
|
||||||
|
// Prefer chains with the fewest number of bytes.
|
||||||
|
Smallest *bool
|
||||||
|
|
||||||
|
// Select first chain having a root with one of
|
||||||
|
// these common names.
|
||||||
|
RootCommonName []string
|
||||||
|
|
||||||
|
// Select first chain that has any issuer with one
|
||||||
|
// of these common names.
|
||||||
|
AnyCommonName []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultACME specifies default settings to use for ACMEManagers.
|
||||||
|
// Using this value is optional but can be convenient.
|
||||||
var DefaultACME = ACMEManager{
|
var DefaultACME = ACMEManager{
|
||||||
CA: LetsEncryptProductionCA,
|
CA: LetsEncryptProductionCA,
|
||||||
TestCA: LetsEncryptStagingCA,
|
TestCA: LetsEncryptStagingCA,
|
||||||
|
@ -337,6 +452,7 @@ var DefaultACME = ACMEManager{
|
||||||
const (
|
const (
|
||||||
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
LetsEncryptStagingCA = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
LetsEncryptProductionCA = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
ZeroSSLProductionCA = "https://acme.zerossl.com/v2/DV90"
|
||||||
)
|
)
|
||||||
|
|
||||||
// prefixACME is the storage key prefix used for ACME-specific assets.
|
// prefixACME is the storage key prefix used for ACME-specific assets.
|
||||||
|
|
|
@ -113,10 +113,11 @@ func (cfg *Config) CacheManagedCertificate(domain string) (Certificate, error) {
|
||||||
return cert, nil
|
return cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadManagedCertificate loads the managed certificate for domain,
|
// loadManagedCertificate loads the managed certificate for domain from any
|
||||||
// but it does not add it to the cache. It just loads from storage.
|
// of the configured issuers' storage locations, but it does not add it to
|
||||||
|
// the cache. It just loads from storage and returns it.
|
||||||
func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) {
|
func (cfg *Config) loadManagedCertificate(domain string) (Certificate, error) {
|
||||||
certRes, err := cfg.loadCertResource(domain)
|
certRes, err := cfg.loadCertResourceAnyIssuer(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Certificate{}, err
|
return Certificate{}, err
|
||||||
}
|
}
|
||||||
|
@ -154,7 +155,7 @@ func (cfg *Config) CacheUnmanagedTLSCertificate(tlsCert tls.Certificate, tags []
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = stapleOCSP(cfg.Storage, &cert, nil)
|
_, err = stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil)
|
||||||
if err != nil && cfg.Logger != nil {
|
if err != nil && cfg.Logger != nil {
|
||||||
cfg.Logger.Warn("stapling OCSP", zap.Error(err))
|
cfg.Logger.Warn("stapling OCSP", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
@ -202,7 +203,7 @@ func (cfg Config) makeCertificateWithOCSP(certPEMBlock, keyPEMBlock []byte) (Cer
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cert, err
|
return cert, err
|
||||||
}
|
}
|
||||||
_, err = stapleOCSP(cfg.Storage, &cert, certPEMBlock)
|
_, err = stapleOCSP(cfg.OCSP, cfg.Storage, &cert, certPEMBlock)
|
||||||
if err != nil && cfg.Logger != nil {
|
if err != nil && cfg.Logger != nil {
|
||||||
cfg.Logger.Warn("stapling OCSP", zap.Error(err))
|
cfg.Logger.Warn("stapling OCSP", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
@ -295,19 +296,12 @@ func fillCertFromLeaf(cert *Certificate, tlsCert tls.Certificate) error {
|
||||||
// meantime, and it would be a good idea to simply load the cert
|
// meantime, and it would be a good idea to simply load the cert
|
||||||
// into our cache rather than repeating the renewal process again.
|
// into our cache rather than repeating the renewal process again.
|
||||||
func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, error) {
|
func (cfg *Config) managedCertInStorageExpiresSoon(cert Certificate) (bool, error) {
|
||||||
certRes, err := cfg.loadCertResource(cert.Names[0])
|
certRes, err := cfg.loadCertResourceAnyIssuer(cert.Names[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
tlsCert, err := tls.X509KeyPair(certRes.CertificatePEM, certRes.PrivateKeyPEM)
|
_, needsRenew := cfg.managedCertNeedsRenewal(certRes)
|
||||||
if err != nil {
|
return needsRenew, nil
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
leaf, err := x509.ParseCertificate(tlsCert.Certificate[0])
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return currentlyInRenewalWindow(leaf.NotBefore, leaf.NotAfter, cfg.RenewalWindowRatio), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reloadManagedCertificate reloads the certificate corresponding to the name(s)
|
// reloadManagedCertificate reloads the certificate corresponding to the name(s)
|
||||||
|
@ -341,8 +335,9 @@ func SubjectQualifiesForCert(subj string) bool {
|
||||||
!strings.HasPrefix(subj, ".") &&
|
!strings.HasPrefix(subj, ".") &&
|
||||||
!strings.HasSuffix(subj, ".") &&
|
!strings.HasSuffix(subj, ".") &&
|
||||||
|
|
||||||
// if it has a wildcard, must be a left-most label
|
// if it has a wildcard, must be a left-most label (or exactly "*"
|
||||||
(!strings.Contains(subj, "*") || strings.HasPrefix(subj, "*.")) &&
|
// which won't be trusted by browsers but still technically works)
|
||||||
|
(!strings.Contains(subj, "*") || strings.HasPrefix(subj, "*.") || subj == "*") &&
|
||||||
|
|
||||||
// must not contain other common special characters
|
// must not contain other common special characters
|
||||||
!strings.ContainsAny(subj, "()[]{}<> \t\n\"\\!@#$%^&|;'+=")
|
!strings.ContainsAny(subj, "()[]{}<> \t\n\"\\!@#$%^&|;'+=")
|
||||||
|
@ -356,32 +351,45 @@ func SubjectQualifiesForCert(subj string) bool {
|
||||||
// allowed, as long as they conform to CABF requirements (only
|
// allowed, as long as they conform to CABF requirements (only
|
||||||
// one wildcard label, and it must be the left-most label).
|
// one wildcard label, and it must be the left-most label).
|
||||||
func SubjectQualifiesForPublicCert(subj string) bool {
|
func SubjectQualifiesForPublicCert(subj string) bool {
|
||||||
// must at least qualify for certificate
|
// must at least qualify for a certificate
|
||||||
return SubjectQualifiesForCert(subj) &&
|
return SubjectQualifiesForCert(subj) &&
|
||||||
|
|
||||||
// localhost is ineligible
|
// localhost, .localhost TLD, and .local TLD are ineligible
|
||||||
subj != "localhost" &&
|
!SubjectIsInternal(subj) &&
|
||||||
|
|
||||||
// .localhost TLD is ineligible
|
|
||||||
!strings.HasSuffix(subj, ".localhost") &&
|
|
||||||
|
|
||||||
// .local TLD is ineligible
|
|
||||||
!strings.HasSuffix(subj, ".local") &&
|
|
||||||
|
|
||||||
// only one wildcard label allowed, and it must be left-most
|
|
||||||
(!strings.Contains(subj, "*") ||
|
|
||||||
(strings.Count(subj, "*") == 1 &&
|
|
||||||
len(subj) > 2 &&
|
|
||||||
strings.HasPrefix(subj, "*."))) &&
|
|
||||||
|
|
||||||
// cannot be an IP address (as of yet), see
|
// cannot be an IP address (as of yet), see
|
||||||
// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
|
// https://community.letsencrypt.org/t/certificate-for-static-ip/84/2?u=mholt
|
||||||
net.ParseIP(subj) == nil
|
!SubjectIsIP(subj) &&
|
||||||
|
|
||||||
|
// only one wildcard label allowed, and it must be left-most, with 3+ labels
|
||||||
|
(!strings.Contains(subj, "*") ||
|
||||||
|
(strings.Count(subj, "*") == 1 &&
|
||||||
|
strings.Count(subj, ".") > 1 &&
|
||||||
|
len(subj) > 2 &&
|
||||||
|
strings.HasPrefix(subj, "*.")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectIsIP returns true if subj is an IP address.
|
||||||
|
func SubjectIsIP(subj string) bool {
|
||||||
|
return net.ParseIP(subj) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubjectIsInternal returns true if subj is an internal-facing
|
||||||
|
// hostname or address.
|
||||||
|
func SubjectIsInternal(subj string) bool {
|
||||||
|
return subj == "localhost" ||
|
||||||
|
strings.HasSuffix(subj, ".localhost") ||
|
||||||
|
strings.HasSuffix(subj, ".local")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MatchWildcard returns true if subject (a candidate DNS name)
|
// MatchWildcard returns true if subject (a candidate DNS name)
|
||||||
// matches wildcard (a reference DNS name), mostly according to
|
// matches wildcard (a reference DNS name), mostly according to
|
||||||
// RFC6125-compliant wildcard rules.
|
// RFC 6125-compliant wildcard rules. See also RFC 2818 which
|
||||||
|
// states that IP addresses must match exactly, but this function
|
||||||
|
// does not attempt to distinguish IP addresses from internal or
|
||||||
|
// external DNS names that happen to look like IP addresses.
|
||||||
|
// It uses DNS wildcard matching logic.
|
||||||
|
// https://tools.ietf.org/html/rfc2818#section-3.1
|
||||||
func MatchWildcard(subject, wildcard string) bool {
|
func MatchWildcard(subject, wildcard string) bool {
|
||||||
if subject == wildcard {
|
if subject == wildcard {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -125,8 +125,10 @@ func HTTPS(domainNames []string, mux http.Handler) error {
|
||||||
WriteTimeout: 5 * time.Second,
|
WriteTimeout: 5 * time.Second,
|
||||||
IdleTimeout: 5 * time.Second,
|
IdleTimeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
if am, ok := cfg.Issuer.(*ACMEManager); ok {
|
if len(cfg.Issuers) > 0 {
|
||||||
httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler))
|
if am, ok := cfg.Issuers[0].(*ACMEManager); ok {
|
||||||
|
httpServer.Handler = am.HTTPChallengeHandler(http.HandlerFunc(httpRedirectHandler))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
httpsServer := &http.Server{
|
httpsServer := &http.Server{
|
||||||
ReadHeaderTimeout: 10 * time.Second,
|
ReadHeaderTimeout: 10 * time.Second,
|
||||||
|
@ -425,9 +427,11 @@ func (cr *CertificateResource) NamesKey() string {
|
||||||
|
|
||||||
// Default contains the package defaults for the
|
// Default contains the package defaults for the
|
||||||
// various Config fields. This is used as a template
|
// various Config fields. This is used as a template
|
||||||
// when creating your own Configs with New(), and it
|
// when creating your own Configs with New() or
|
||||||
// is also used as the Config by all the high-level
|
// NewDefault(), and it is also used as the Config
|
||||||
// functions in this package.
|
// by all the high-level functions in this package
|
||||||
|
// that abstract away most configuration (HTTPS(),
|
||||||
|
// TLS(), Listen(), etc).
|
||||||
//
|
//
|
||||||
// The fields of this value will be used for Config
|
// The fields of this value will be used for Config
|
||||||
// fields which are unset. Feel free to modify these
|
// fields which are unset. Feel free to modify these
|
||||||
|
@ -436,8 +440,10 @@ func (cr *CertificateResource) NamesKey() string {
|
||||||
// obtained by calling New() (if you have your own
|
// obtained by calling New() (if you have your own
|
||||||
// certificate cache) or NewDefault() (if you only
|
// certificate cache) or NewDefault() (if you only
|
||||||
// need a single config and want to use the default
|
// need a single config and want to use the default
|
||||||
// cache). This is the only Config which can access
|
// cache).
|
||||||
// the default certificate cache.
|
//
|
||||||
|
// Even if the Issuers or Storage fields are not set,
|
||||||
|
// defaults will be applied in the call to New().
|
||||||
var Default = Config{
|
var Default = Config{
|
||||||
RenewalWindowRatio: DefaultRenewalWindowRatio,
|
RenewalWindowRatio: DefaultRenewalWindowRatio,
|
||||||
Storage: defaultFileStorage,
|
Storage: defaultFileStorage,
|
||||||
|
@ -459,12 +465,12 @@ const (
|
||||||
// are set to; otherwise ACME challenges will fail.
|
// are set to; otherwise ACME challenges will fail.
|
||||||
var (
|
var (
|
||||||
// HTTPPort is the port on which to serve HTTP
|
// HTTPPort is the port on which to serve HTTP
|
||||||
// and, by extension, the HTTP challenge (unless
|
// and, as such, the HTTP challenge (unless
|
||||||
// Default.AltHTTPPort is set).
|
// Default.AltHTTPPort is set).
|
||||||
HTTPPort = 80
|
HTTPPort = 80
|
||||||
|
|
||||||
// HTTPSPort is the port on which to serve HTTPS
|
// HTTPSPort is the port on which to serve HTTPS
|
||||||
// and, by extension, the TLS-ALPN challenge
|
// and, as such, the TLS-ALPN challenge
|
||||||
// (unless Default.AltTLSALPNPort is set).
|
// (unless Default.AltTLSALPNPort is set).
|
||||||
HTTPSPort = 443
|
HTTPSPort = 443
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
weakrand "math/rand"
|
weakrand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
@ -31,7 +32,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
|
"github.com/mholt/acmez/acme"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config configures a certificate manager instance.
|
// Config configures a certificate manager instance.
|
||||||
|
@ -54,45 +57,48 @@ type Config struct {
|
||||||
|
|
||||||
// DefaultServerName specifies a server name
|
// DefaultServerName specifies a server name
|
||||||
// to use when choosing a certificate if the
|
// to use when choosing a certificate if the
|
||||||
// ClientHello's ServerName field is empty
|
// ClientHello's ServerName field is empty.
|
||||||
DefaultServerName string
|
DefaultServerName string
|
||||||
|
|
||||||
// The state needed to operate on-demand TLS;
|
// The state needed to operate on-demand TLS;
|
||||||
// if non-nil, on-demand TLS is enabled and
|
// if non-nil, on-demand TLS is enabled and
|
||||||
// certificate operations are deferred to
|
// certificate operations are deferred to
|
||||||
// TLS handshakes (or as-needed)
|
// TLS handshakes (or as-needed).
|
||||||
// TODO: Can we call this feature "Reactive/Lazy/Passive TLS" instead?
|
// TODO: Can we call this feature "Reactive/Lazy/Passive TLS" instead?
|
||||||
OnDemand *OnDemandConfig
|
OnDemand *OnDemandConfig
|
||||||
|
|
||||||
// Add the must staple TLS extension to the CSR
|
// Adds the must staple TLS extension to the CSR.
|
||||||
MustStaple bool
|
MustStaple bool
|
||||||
|
|
||||||
// The type that issues certificates; the
|
// The source for getting new certificates; the
|
||||||
// default Issuer is ACMEManager
|
// default Issuer is ACMEManager. If multiple
|
||||||
Issuer Issuer
|
// issuers are specified, they will be tried in
|
||||||
|
// turn until one succeeds.
|
||||||
// The type that revokes certificates; must
|
Issuers []Issuer
|
||||||
// be configured in conjunction with the Issuer
|
|
||||||
// field such that both the Issuer and Revoker
|
|
||||||
// are related (because issuance information is
|
|
||||||
// required for revocation)
|
|
||||||
Revoker Revoker
|
|
||||||
|
|
||||||
// The source of new private keys for certificates;
|
// The source of new private keys for certificates;
|
||||||
// the default KeySource is StandardKeyGenerator
|
// the default KeySource is StandardKeyGenerator.
|
||||||
KeySource KeyGenerator
|
KeySource KeyGenerator
|
||||||
|
|
||||||
// CertSelection chooses one of the certificates
|
// CertSelection chooses one of the certificates
|
||||||
// with which the ClientHello will be completed;
|
// with which the ClientHello will be completed;
|
||||||
// if not set, DefaultCertificateSelector will
|
// if not set, DefaultCertificateSelector will
|
||||||
// be used
|
// be used.
|
||||||
CertSelection CertificateSelector
|
CertSelection CertificateSelector
|
||||||
|
|
||||||
// The storage to access when storing or
|
// OCSP configures how OCSP is handled. By default,
|
||||||
// loading TLS assets
|
// OCSP responses are fetched for every certificate
|
||||||
|
// with a responder URL, and cached on disk. Changing
|
||||||
|
// these defaults is STRONGLY discouraged unless you
|
||||||
|
// have a compelling reason to put clients at greater
|
||||||
|
// risk and reduce their privacy.
|
||||||
|
OCSP OCSPConfig
|
||||||
|
|
||||||
|
// The storage to access when storing or loading
|
||||||
|
// TLS assets. Default is the local file system.
|
||||||
Storage Storage
|
Storage Storage
|
||||||
|
|
||||||
// Set a logger to enable logging
|
// Set a logger to enable logging.
|
||||||
Logger *zap.Logger
|
Logger *zap.Logger
|
||||||
|
|
||||||
// required pointer to the in-memory cert cache
|
// required pointer to the in-memory cert cache
|
||||||
|
@ -116,6 +122,9 @@ type Config struct {
|
||||||
// same, default certificate cache. All configs returned
|
// same, default certificate cache. All configs returned
|
||||||
// by NewDefault() are based on the values of the fields of
|
// by NewDefault() are based on the values of the fields of
|
||||||
// Default at the time it is called.
|
// Default at the time it is called.
|
||||||
|
//
|
||||||
|
// This is the only way to get a config that uses the
|
||||||
|
// default certificate cache.
|
||||||
func NewDefault() *Config {
|
func NewDefault() *Config {
|
||||||
defaultCacheMu.Lock()
|
defaultCacheMu.Lock()
|
||||||
if defaultCache == nil {
|
if defaultCache == nil {
|
||||||
|
@ -153,7 +162,7 @@ func NewDefault() *Config {
|
||||||
// the vast majority of cases, there will be only a
|
// the vast majority of cases, there will be only a
|
||||||
// single Config, thus the default cache (which always
|
// single Config, thus the default cache (which always
|
||||||
// uses the default Config) and default config will
|
// uses the default Config) and default config will
|
||||||
// suffice, and you should use New() instead.
|
// suffice, and you should use NewDefault() instead.
|
||||||
func New(certCache *Cache, cfg Config) *Config {
|
func New(certCache *Cache, cfg Config) *Config {
|
||||||
if certCache == nil {
|
if certCache == nil {
|
||||||
panic("a certificate cache is required")
|
panic("a certificate cache is required")
|
||||||
|
@ -196,23 +205,11 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
|
||||||
if cfg.Storage == nil {
|
if cfg.Storage == nil {
|
||||||
cfg.Storage = Default.Storage
|
cfg.Storage = Default.Storage
|
||||||
}
|
}
|
||||||
if cfg.Issuer == nil {
|
if len(cfg.Issuers) == 0 {
|
||||||
cfg.Issuer = Default.Issuer
|
cfg.Issuers = Default.Issuers
|
||||||
if cfg.Issuer == nil {
|
if len(cfg.Issuers) == 0 {
|
||||||
// okay really, we need an issuer,
|
// at least one issuer is absolutely required
|
||||||
// that's kind of the point; most
|
cfg.Issuers = []Issuer{NewACMEManager(&cfg, DefaultACME)}
|
||||||
// people would probably want ACME
|
|
||||||
cfg.Issuer = NewACMEManager(&cfg, DefaultACME)
|
|
||||||
}
|
|
||||||
// issuer and revoker go together; if user
|
|
||||||
// specifies their own issuer, we don't want
|
|
||||||
// to override their revoker, hence we only
|
|
||||||
// do this if Issuer was also nil
|
|
||||||
if cfg.Revoker == nil {
|
|
||||||
cfg.Revoker = Default.Revoker
|
|
||||||
if cfg.Revoker == nil {
|
|
||||||
cfg.Revoker = NewACMEManager(&cfg, DefaultACME)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +220,6 @@ func newWithCache(certCache *Cache, cfg Config) *Config {
|
||||||
cfg.Storage = defaultFileStorage
|
cfg.Storage = defaultFileStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the unexported fields are valid
|
|
||||||
cfg.certCache = certCache
|
cfg.certCache = certCache
|
||||||
|
|
||||||
return &cfg
|
return &cfg
|
||||||
|
@ -254,6 +250,29 @@ func (cfg *Config) ManageSync(domainNames []string) error {
|
||||||
return cfg.manageAll(nil, domainNames, false)
|
return cfg.manageAll(nil, domainNames, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientCredentials returns a list of TLS client certificate chains for the given identifiers.
|
||||||
|
// The return value can be used in a tls.Config to enable client authentication using managed certificates.
|
||||||
|
// Any certificates that need to be obtained or renewed for these identifiers will be managed accordingly.
|
||||||
|
func (cfg *Config) ClientCredentials(ctx context.Context, identifiers []string) ([]tls.Certificate, error) {
|
||||||
|
err := cfg.manageAll(ctx, identifiers, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var chains []tls.Certificate
|
||||||
|
for _, id := range identifiers {
|
||||||
|
certRes, err := cfg.loadCertResourceAnyIssuer(id)
|
||||||
|
if err != nil {
|
||||||
|
return chains, err
|
||||||
|
}
|
||||||
|
chain, err := tls.X509KeyPair(certRes.CertificatePEM, certRes.PrivateKeyPEM)
|
||||||
|
if err != nil {
|
||||||
|
return chains, err
|
||||||
|
}
|
||||||
|
chains = append(chains, chain)
|
||||||
|
}
|
||||||
|
return chains, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ManageAsync is the same as ManageSync, except that ACME
|
// ManageAsync is the same as ManageSync, except that ACME
|
||||||
// operations are performed asynchronously (in the background).
|
// operations are performed asynchronously (in the background).
|
||||||
// This method returns before certificates are ready. It is
|
// This method returns before certificates are ready. It is
|
||||||
|
@ -360,6 +379,28 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unmanage causes the certificates for domainNames to stop being managed.
|
||||||
|
// If there are certificates for the supplied domain names in the cache, they
|
||||||
|
// are evicted from the cache.
|
||||||
|
func (cfg *Config) Unmanage(domainNames []string) {
|
||||||
|
var deleteQueue []Certificate
|
||||||
|
for _, domainName := range domainNames {
|
||||||
|
certs := cfg.certCache.AllMatchingCertificates(domainName)
|
||||||
|
for _, cert := range certs {
|
||||||
|
if !cert.managed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
deleteQueue = append(deleteQueue, cert)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.certCache.mu.Lock()
|
||||||
|
for _, cert := range deleteQueue {
|
||||||
|
cfg.certCache.removeCertificate(cert)
|
||||||
|
}
|
||||||
|
cfg.certCache.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// ObtainCert obtains a certificate for name using cfg, as long
|
// ObtainCert obtains a certificate for name using cfg, as long
|
||||||
// as a certificate does not already exist in storage for that
|
// as a certificate does not already exist in storage for that
|
||||||
// name. The name must qualify and cfg must be flagged as Managed.
|
// name. The name must qualify and cfg must be flagged as Managed.
|
||||||
|
@ -372,27 +413,22 @@ func (cfg *Config) manageOne(ctx context.Context, domainName string, async bool)
|
||||||
// TODO: consider moving interactive param into the Config struct,
|
// TODO: consider moving interactive param into the Config struct,
|
||||||
// and maybe retry settings into the Config struct as well? (same for RenewCert)
|
// and maybe retry settings into the Config struct as well? (same for RenewCert)
|
||||||
func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool) error {
|
func (cfg *Config) ObtainCert(ctx context.Context, name string, interactive bool) error {
|
||||||
if cfg.storageHasCertResources(name) {
|
if len(cfg.Issuers) == 0 {
|
||||||
|
return fmt.Errorf("no issuers configured; impossible to obtain or check for existing certificate in storage")
|
||||||
|
}
|
||||||
|
if cfg.storageHasCertResourcesAnyIssuer(name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive)
|
// ensure storage is writeable and readable
|
||||||
|
// TODO: this is not necessary every time; should only perform check once every so often for each storage, which may require some global state...
|
||||||
|
err := cfg.checkStorage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
|
||||||
}
|
}
|
||||||
if issuer == nil {
|
return cfg.obtainCert(ctx, name, interactive)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return cfg.obtainWithIssuer(ctx, issuer, name, interactive)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loggerNamed(l *zap.Logger, name string) *zap.Logger {
|
func (cfg *Config) obtainCert(ctx context.Context, name string, interactive bool) error {
|
||||||
if l == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return l.Named(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error {
|
|
||||||
log := loggerNamed(cfg.Logger, "obtain")
|
log := loggerNamed(cfg.Logger, "obtain")
|
||||||
|
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
@ -400,10 +436,10 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure idempotency of the obtain operation for this name
|
// ensure idempotency of the obtain operation for this name
|
||||||
lockKey := cfg.lockKey("cert_acme", name)
|
lockKey := cfg.lockKey(certIssueLockOp, name)
|
||||||
err := acquireLock(ctx, cfg.Storage, lockKey)
|
err := acquireLock(ctx, cfg.Storage, lockKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
@ -424,7 +460,7 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str
|
||||||
|
|
||||||
f := func(ctx context.Context) error {
|
f := func(ctx context.Context) error {
|
||||||
// check if obtain is still needed -- might have been obtained during lock
|
// check if obtain is still needed -- might have been obtained during lock
|
||||||
if cfg.storageHasCertResources(name) {
|
if cfg.storageHasCertResourcesAnyIssuer(name) {
|
||||||
if log != nil {
|
if log != nil {
|
||||||
log.Info("certificate already exists in storage", zap.String("identifier", name))
|
log.Info("certificate already exists in storage", zap.String("identifier", name))
|
||||||
}
|
}
|
||||||
|
@ -445,8 +481,24 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuedCert, err := issuer.Issue(ctx, csr)
|
// try to obtain from each issuer until we succeed
|
||||||
|
var issuedCert *IssuedCertificate
|
||||||
|
var issuerUsed Issuer
|
||||||
|
for _, issuer := range cfg.Issuers {
|
||||||
|
if prechecker, ok := issuer.(PreChecker); ok {
|
||||||
|
err = prechecker.PreCheck(ctx, []string{name}, interactive)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
issuedCert, err = issuer.Issue(ctx, csr)
|
||||||
|
if err == nil {
|
||||||
|
issuerUsed = issuer
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: only the error from the last issuer will be returned, oh well?
|
||||||
return fmt.Errorf("[%s] Obtain: %w", name, err)
|
return fmt.Errorf("[%s] Obtain: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +509,7 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str
|
||||||
PrivateKeyPEM: privKeyPEM,
|
PrivateKeyPEM: privKeyPEM,
|
||||||
IssuerData: issuedCert.Metadata,
|
IssuerData: issuedCert.Metadata,
|
||||||
}
|
}
|
||||||
err = cfg.saveCertResource(certRes)
|
err = cfg.saveCertResource(issuerUsed, certRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err)
|
return fmt.Errorf("[%s] Obtain: saving assets: %v", name, err)
|
||||||
}
|
}
|
||||||
|
@ -480,21 +532,32 @@ func (cfg *Config) obtainWithIssuer(ctx context.Context, issuer Issuer, name str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) storageHasCertResourcesAnyIssuer(name string) bool {
|
||||||
|
for _, iss := range cfg.Issuers {
|
||||||
|
if cfg.storageHasCertResources(iss, name) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// RenewCert renews the certificate for name using cfg. It stows the
|
// RenewCert renews the certificate for name using cfg. It stows the
|
||||||
// renewed certificate and its assets in storage if successful. It
|
// renewed certificate and its assets in storage if successful. It
|
||||||
// DOES NOT update the in-memory cache with the new certificate.
|
// DOES NOT update the in-memory cache with the new certificate.
|
||||||
func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) error {
|
func (cfg *Config) RenewCert(ctx context.Context, name string, interactive bool) error {
|
||||||
issuer, err := cfg.getPrecheckedIssuer(ctx, []string{name}, interactive)
|
if len(cfg.Issuers) == 0 {
|
||||||
|
return fmt.Errorf("no issuers configured; impossible to renew or check existing certificate in storage")
|
||||||
|
}
|
||||||
|
// ensure storage is writeable and readable
|
||||||
|
// TODO: this is not necessary every time; should only perform check once every so often for each storage, which may require some global state...
|
||||||
|
err := cfg.checkStorage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
|
||||||
}
|
}
|
||||||
if issuer == nil {
|
return cfg.renewCert(ctx, name, interactive)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return cfg.renewWithIssuer(ctx, issuer, name, interactive)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name string, interactive bool) error {
|
func (cfg *Config) renewCert(ctx context.Context, name string, interactive bool) error {
|
||||||
log := loggerNamed(cfg.Logger, "renew")
|
log := loggerNamed(cfg.Logger, "renew")
|
||||||
|
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
@ -502,10 +565,10 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure idempotency of the renew operation for this name
|
// ensure idempotency of the renew operation for this name
|
||||||
lockKey := cfg.lockKey("cert_acme", name)
|
lockKey := cfg.lockKey(certIssueLockOp, name)
|
||||||
err := acquireLock(ctx, cfg.Storage, lockKey)
|
err := acquireLock(ctx, cfg.Storage, lockKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to acquire lock '%s': %v", lockKey, err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
@ -526,7 +589,7 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri
|
||||||
|
|
||||||
f := func(ctx context.Context) error {
|
f := func(ctx context.Context) error {
|
||||||
// prepare for renewal (load PEM cert, key, and meta)
|
// prepare for renewal (load PEM cert, key, and meta)
|
||||||
certRes, err := cfg.loadCertResource(name)
|
certRes, err := cfg.loadCertResourceAnyIssuer(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -556,8 +619,24 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
issuedCert, err := issuer.Issue(ctx, csr)
|
// try to obtain from each issuer until we succeed
|
||||||
|
var issuedCert *IssuedCertificate
|
||||||
|
var issuerUsed Issuer
|
||||||
|
for _, issuer := range cfg.Issuers {
|
||||||
|
if prechecker, ok := issuer.(PreChecker); ok {
|
||||||
|
err = prechecker.PreCheck(ctx, []string{name}, interactive)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
issuedCert, err = issuer.Issue(ctx, csr)
|
||||||
|
if err == nil {
|
||||||
|
issuerUsed = issuer
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// TODO: only the error from the last issuer will be returned, oh well?
|
||||||
return fmt.Errorf("[%s] Renew: %w", name, err)
|
return fmt.Errorf("[%s] Renew: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +647,7 @@ func (cfg *Config) renewWithIssuer(ctx context.Context, issuer Issuer, name stri
|
||||||
PrivateKeyPEM: certRes.PrivateKeyPEM,
|
PrivateKeyPEM: certRes.PrivateKeyPEM,
|
||||||
IssuerData: issuedCert.Metadata,
|
IssuerData: issuedCert.Metadata,
|
||||||
}
|
}
|
||||||
err = cfg.saveCertResource(newCertRes)
|
err = cfg.saveCertResource(issuerUsed, newCertRes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("[%s] Renew: saving assets: %v", name, err)
|
return fmt.Errorf("[%s] Renew: saving assets: %v", name, err)
|
||||||
}
|
}
|
||||||
|
@ -602,7 +681,12 @@ func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x5
|
||||||
} else if u, err := url.Parse(name); err == nil && strings.Contains(name, "/") {
|
} else if u, err := url.Parse(name); err == nil && strings.Contains(name, "/") {
|
||||||
csrTemplate.URIs = append(csrTemplate.URIs, u)
|
csrTemplate.URIs = append(csrTemplate.URIs, u)
|
||||||
} else {
|
} else {
|
||||||
csrTemplate.DNSNames = append(csrTemplate.DNSNames, name)
|
// convert IDNs to ASCII according to RFC 5280 section 7
|
||||||
|
normalizedName, err := idna.ToASCII(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("converting identifier '%s' to ASCII: %v", name, err)
|
||||||
|
}
|
||||||
|
csrTemplate.DNSNames = append(csrTemplate.DNSNames, normalizedName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,43 +703,45 @@ func (cfg *Config) generateCSR(privateKey crypto.PrivateKey, sans []string) (*x5
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevokeCert revokes the certificate for domain via ACME protocol. It requires
|
// RevokeCert revokes the certificate for domain via ACME protocol. It requires
|
||||||
// that cfg.Issuer is properly configured with the same issuer that issued the
|
// that cfg.Issuers is properly configured with the same issuer that issued the
|
||||||
// certificate being revoked. See RFC 5280 §5.3.1 for reason codes.
|
// certificate being revoked. See RFC 5280 §5.3.1 for reason codes.
|
||||||
func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, interactive bool) error {
|
func (cfg *Config) RevokeCert(ctx context.Context, domain string, reason int, interactive bool) error {
|
||||||
rev := cfg.Revoker
|
for i, issuer := range cfg.Issuers {
|
||||||
if rev == nil {
|
issuerKey := issuer.IssuerKey()
|
||||||
rev = Default.Revoker
|
|
||||||
}
|
|
||||||
|
|
||||||
certRes, err := cfg.loadCertResource(domain)
|
rev, ok := issuer.(Revoker)
|
||||||
if err != nil {
|
if !ok {
|
||||||
return err
|
return fmt.Errorf("issuer %d (%s) is not a Revoker", i, issuerKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
issuerKey := cfg.Issuer.IssuerKey()
|
certRes, err := cfg.loadCertResource(issuer, domain)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if !cfg.Storage.Exists(StorageKeys.SitePrivateKey(issuerKey, domain)) {
|
if !cfg.Storage.Exists(StorageKeys.SitePrivateKey(issuerKey, domain)) {
|
||||||
return fmt.Errorf("private key not found for %s", certRes.SANs)
|
return fmt.Errorf("private key not found for %s", certRes.SANs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rev.Revoke(ctx, certRes, reason)
|
err = rev.Revoke(ctx, certRes, reason)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("issuer %d (%s): %v", i, issuerKey, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.emit("cert_revoked", domain)
|
cfg.emit("cert_revoked", domain)
|
||||||
|
|
||||||
err = cfg.Storage.Delete(StorageKeys.SiteCert(issuerKey, domain))
|
err = cfg.Storage.Delete(StorageKeys.SiteCert(issuerKey, domain))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err)
|
return fmt.Errorf("certificate revoked, but unable to delete certificate file: %v", err)
|
||||||
}
|
}
|
||||||
err = cfg.Storage.Delete(StorageKeys.SitePrivateKey(issuerKey, domain))
|
err = cfg.Storage.Delete(StorageKeys.SitePrivateKey(issuerKey, domain))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err)
|
return fmt.Errorf("certificate revoked, but unable to delete private key: %v", err)
|
||||||
}
|
}
|
||||||
err = cfg.Storage.Delete(StorageKeys.SiteMeta(issuerKey, domain))
|
err = cfg.Storage.Delete(StorageKeys.SiteMeta(issuerKey, domain))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err)
|
return fmt.Errorf("certificate revoked, but unable to delete certificate metadata: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -692,27 +778,50 @@ func (cfg *Config) TLSConfig() *tls.Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getPrecheckedIssuer returns an Issuer with pre-checks
|
// getChallengeInfo loads the challenge info from either the internal challenge memory
|
||||||
// completed, if it is also a PreChecker. It also checks
|
// or the external storage (implying distributed solving). The second return value
|
||||||
// that storage is functioning. If a nil Issuer is returned
|
// indicates whether challenge info was loaded from external storage. If true, the
|
||||||
// with a nil error, that means to skip this operation
|
// challenge is being solved in a distributed fashion; if false, from internal memory.
|
||||||
// (not an error, just a no-op).
|
// If no matching challenge information can be found, an error is returned.
|
||||||
func (cfg *Config) getPrecheckedIssuer(ctx context.Context, names []string, interactive bool) (Issuer, error) {
|
func (cfg *Config) getChallengeInfo(identifier string) (Challenge, bool, error) {
|
||||||
// ensure storage is writeable and readable
|
// first, check if our process initiated this challenge; if so, just return it
|
||||||
// TODO: this is not necessary every time; should only
|
chalData, ok := GetACMEChallenge(identifier)
|
||||||
// perform check once every so often for each storage,
|
if ok {
|
||||||
// which may require some global state...
|
return chalData, false, nil
|
||||||
err := cfg.checkStorage()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed storage check: %v - storage is probably misconfigured", err)
|
|
||||||
}
|
}
|
||||||
if prechecker, ok := cfg.Issuer.(PreChecker); ok {
|
|
||||||
err := prechecker.PreCheck(ctx, names, interactive)
|
// otherwise, perhaps another instance in the cluster initiated it; check
|
||||||
if err != nil {
|
// the configured storage to retrieve challenge data
|
||||||
return nil, err
|
|
||||||
|
var chalInfo acme.Challenge
|
||||||
|
var chalInfoBytes []byte
|
||||||
|
var tokenKey string
|
||||||
|
for _, issuer := range cfg.Issuers {
|
||||||
|
ds := distributedSolver{
|
||||||
|
storage: cfg.Storage,
|
||||||
|
storageKeyIssuerPrefix: storageKeyACMECAPrefix(issuer.IssuerKey()),
|
||||||
}
|
}
|
||||||
|
tokenKey = ds.challengeTokensKey(identifier)
|
||||||
|
var err error
|
||||||
|
chalInfoBytes, err = cfg.Storage.Load(tokenKey)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return Challenge{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err)
|
||||||
}
|
}
|
||||||
return cfg.Issuer, nil
|
if len(chalInfoBytes) == 0 {
|
||||||
|
return Challenge{}, false, fmt.Errorf("no information found to solve challenge for identifier: %s", identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := json.Unmarshal(chalInfoBytes, &chalInfo)
|
||||||
|
if err != nil {
|
||||||
|
return Challenge{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Challenge{Challenge: chalInfo}, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkStorage tests the storage by writing random bytes
|
// checkStorage tests the storage by writing random bytes
|
||||||
|
@ -758,8 +867,8 @@ func (cfg *Config) checkStorage() error {
|
||||||
// associated with cfg's certificate cache has all the
|
// associated with cfg's certificate cache has all the
|
||||||
// resources related to the certificate for domain: the
|
// resources related to the certificate for domain: the
|
||||||
// certificate, the private key, and the metadata.
|
// certificate, the private key, and the metadata.
|
||||||
func (cfg *Config) storageHasCertResources(domain string) bool {
|
func (cfg *Config) storageHasCertResources(issuer Issuer, domain string) bool {
|
||||||
issuerKey := cfg.Issuer.IssuerKey()
|
issuerKey := issuer.IssuerKey()
|
||||||
certKey := StorageKeys.SiteCert(issuerKey, domain)
|
certKey := StorageKeys.SiteCert(issuerKey, domain)
|
||||||
keyKey := StorageKeys.SitePrivateKey(issuerKey, domain)
|
keyKey := StorageKeys.SitePrivateKey(issuerKey, domain)
|
||||||
metaKey := StorageKeys.SiteMeta(issuerKey, domain)
|
metaKey := StorageKeys.SiteMeta(issuerKey, domain)
|
||||||
|
@ -771,18 +880,19 @@ func (cfg *Config) storageHasCertResources(domain string) bool {
|
||||||
// lockKey returns a key for a lock that is specific to the operation
|
// lockKey returns a key for a lock that is specific to the operation
|
||||||
// named op being performed related to domainName and this config's CA.
|
// named op being performed related to domainName and this config's CA.
|
||||||
func (cfg *Config) lockKey(op, domainName string) string {
|
func (cfg *Config) lockKey(op, domainName string) string {
|
||||||
return fmt.Sprintf("%s_%s_%s", op, domainName, cfg.Issuer.IssuerKey())
|
return fmt.Sprintf("%s_%s", op, domainName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// managedCertNeedsRenewal returns true if certRes is
|
// managedCertNeedsRenewal returns true if certRes is expiring soon or already expired,
|
||||||
// expiring soon or already expired, or if the process
|
// or if the process of decoding the cert and checking its expiration returned an error.
|
||||||
// of checking the expiration returned an error.
|
|
||||||
func (cfg *Config) managedCertNeedsRenewal(certRes CertificateResource) (time.Duration, bool) {
|
func (cfg *Config) managedCertNeedsRenewal(certRes CertificateResource) (time.Duration, bool) {
|
||||||
cert, err := makeCertificate(certRes.CertificatePEM, certRes.PrivateKeyPEM)
|
certChain, err := parseCertsFromPEMBundle(certRes.CertificatePEM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, true
|
return 0, true
|
||||||
}
|
}
|
||||||
return time.Until(cert.Leaf.NotAfter), cert.NeedsRenewal(cfg)
|
remaining := time.Until(certChain[0].NotAfter)
|
||||||
|
needsRenew := currentlyInRenewalWindow(certChain[0].NotBefore, certChain[0].NotAfter, cfg.RenewalWindowRatio)
|
||||||
|
return remaining, needsRenew
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) emit(eventName string, data interface{}) {
|
func (cfg *Config) emit(eventName string, data interface{}) {
|
||||||
|
@ -792,11 +902,40 @@ func (cfg *Config) emit(eventName string, data interface{}) {
|
||||||
cfg.OnEvent(eventName, data)
|
cfg.OnEvent(eventName, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loggerNamed(l *zap.Logger, name string) *zap.Logger {
|
||||||
|
if l == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.Named(name)
|
||||||
|
}
|
||||||
|
|
||||||
// CertificateSelector is a type which can select a certificate to use given multiple choices.
|
// CertificateSelector is a type which can select a certificate to use given multiple choices.
|
||||||
type CertificateSelector interface {
|
type CertificateSelector interface {
|
||||||
SelectCertificate(*tls.ClientHelloInfo, []Certificate) (Certificate, error)
|
SelectCertificate(*tls.ClientHelloInfo, []Certificate) (Certificate, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OCSPConfig configures how OCSP is handled.
|
||||||
|
type OCSPConfig struct {
|
||||||
|
// Disable automatic OCSP stapling; strongly
|
||||||
|
// discouraged unless you have a good reason.
|
||||||
|
// Disabling this puts clients at greater risk
|
||||||
|
// and reduces their privacy.
|
||||||
|
DisableStapling bool
|
||||||
|
|
||||||
|
// A map of OCSP responder domains to replacement
|
||||||
|
// domains for querying OCSP servers. Used for
|
||||||
|
// overriding the OCSP responder URL that is
|
||||||
|
// embedded in certificates. Mapping to an empty
|
||||||
|
// URL will disable OCSP from that responder.
|
||||||
|
ResponderOverrides map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// certIssueLockOp is the name of the operation used
|
||||||
|
// when naming a lock to make it mutually exclusive
|
||||||
|
// with other certificate issuance operations for a
|
||||||
|
// certain name.
|
||||||
|
const certIssueLockOp = "issue_cert"
|
||||||
|
|
||||||
// Constants for PKIX MustStaple extension.
|
// Constants for PKIX MustStaple extension.
|
||||||
var (
|
var (
|
||||||
tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
|
tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
|
||||||
|
|
|
@ -28,9 +28,12 @@ import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/klauspost/cpuid"
|
"github.com/klauspost/cpuid"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/net/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
// encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes.
|
// encodePrivateKey marshals a EC or RSA private key into a PEM-encoded array of bytes.
|
||||||
|
@ -129,13 +132,13 @@ func fastHash(input []byte) string {
|
||||||
// saveCertResource saves the certificate resource to disk. This
|
// saveCertResource saves the certificate resource to disk. This
|
||||||
// includes the certificate file itself, the private key, and the
|
// includes the certificate file itself, the private key, and the
|
||||||
// metadata file.
|
// metadata file.
|
||||||
func (cfg *Config) saveCertResource(cert CertificateResource) error {
|
func (cfg *Config) saveCertResource(issuer Issuer, cert CertificateResource) error {
|
||||||
metaBytes, err := json.MarshalIndent(cert, "", "\t")
|
metaBytes, err := json.MarshalIndent(cert, "", "\t")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("encoding certificate metadata: %v", err)
|
return fmt.Errorf("encoding certificate metadata: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
issuerKey := cfg.Issuer.IssuerKey()
|
issuerKey := issuer.IssuerKey()
|
||||||
certKey := cert.NamesKey()
|
certKey := cert.NamesKey()
|
||||||
|
|
||||||
all := []keyValue{
|
all := []keyValue{
|
||||||
|
@ -156,20 +159,95 @@ func (cfg *Config) saveCertResource(cert CertificateResource) error {
|
||||||
return storeTx(cfg.Storage, all)
|
return storeTx(cfg.Storage, all)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, error) {
|
// loadCertResourceAnyIssuer loads and returns the certificate resource from any
|
||||||
|
// of the configured issuers. If multiple are found (e.g. if there are 3 issuers
|
||||||
|
// configured, and all 3 have a resource matching certNamesKey), then the newest
|
||||||
|
// (latest NotBefore date) resource will be chosen.
|
||||||
|
func (cfg *Config) loadCertResourceAnyIssuer(certNamesKey string) (CertificateResource, error) {
|
||||||
|
// we can save some extra decoding steps if there's only one issuer, since
|
||||||
|
// we don't need to compare potentially multiple available resources to
|
||||||
|
// select the best one, when there's only one choice anyway
|
||||||
|
if len(cfg.Issuers) == 1 {
|
||||||
|
return cfg.loadCertResource(cfg.Issuers[0], certNamesKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
type decodedCertResource struct {
|
||||||
|
CertificateResource
|
||||||
|
issuer Issuer
|
||||||
|
decoded *x509.Certificate
|
||||||
|
}
|
||||||
|
var certResources []decodedCertResource
|
||||||
|
var lastErr error
|
||||||
|
|
||||||
|
// load and decode all certificate resources found with the
|
||||||
|
// configured issuers so we can sort by newest
|
||||||
|
for _, issuer := range cfg.Issuers {
|
||||||
|
certRes, err := cfg.loadCertResource(issuer, certNamesKey)
|
||||||
|
if err != nil {
|
||||||
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
|
// not a problem, but we need to remember the error
|
||||||
|
// in case we end up not finding any cert resources
|
||||||
|
// since we'll need an error to return in that case
|
||||||
|
lastErr = err
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
certs, err := parseCertsFromPEMBundle(certRes.CertificatePEM)
|
||||||
|
if err != nil {
|
||||||
|
return CertificateResource{}, err
|
||||||
|
}
|
||||||
|
certResources = append(certResources, decodedCertResource{
|
||||||
|
CertificateResource: certRes,
|
||||||
|
issuer: issuer,
|
||||||
|
decoded: certs[0],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(certResources) == 0 {
|
||||||
|
if lastErr == nil {
|
||||||
|
lastErr = fmt.Errorf("no certificate resources found") // just in case; e.g. no Issuers configured
|
||||||
|
}
|
||||||
|
return CertificateResource{}, lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by date so the most recently issued comes first
|
||||||
|
sort.Slice(certResources, func(i, j int) bool {
|
||||||
|
return certResources[j].decoded.NotBefore.Before(certResources[i].decoded.NotBefore)
|
||||||
|
})
|
||||||
|
|
||||||
|
if cfg.Logger != nil {
|
||||||
|
cfg.Logger.Debug("loading managed certificate",
|
||||||
|
zap.String("domain", certNamesKey),
|
||||||
|
zap.Time("expiration", certResources[0].decoded.NotAfter),
|
||||||
|
zap.String("issuer_key", certResources[0].issuer.IssuerKey()),
|
||||||
|
zap.Any("storage", cfg.Storage),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return certResources[0].CertificateResource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadCertResource loads a certificate resource from the given issuer's storage location.
|
||||||
|
func (cfg *Config) loadCertResource(issuer Issuer, certNamesKey string) (CertificateResource, error) {
|
||||||
var certRes CertificateResource
|
var certRes CertificateResource
|
||||||
issuerKey := cfg.Issuer.IssuerKey()
|
issuerKey := issuer.IssuerKey()
|
||||||
certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, certNamesKey))
|
|
||||||
|
normalizedName, err := idna.ToASCII(certNamesKey)
|
||||||
|
if err != nil {
|
||||||
|
return certRes, fmt.Errorf("converting '%s' to ASCII: %v", certNamesKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
certBytes, err := cfg.Storage.Load(StorageKeys.SiteCert(issuerKey, normalizedName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CertificateResource{}, err
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
certRes.CertificatePEM = certBytes
|
certRes.CertificatePEM = certBytes
|
||||||
keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, certNamesKey))
|
keyBytes, err := cfg.Storage.Load(StorageKeys.SitePrivateKey(issuerKey, normalizedName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CertificateResource{}, err
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
certRes.PrivateKeyPEM = keyBytes
|
certRes.PrivateKeyPEM = keyBytes
|
||||||
metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, certNamesKey))
|
metaBytes, err := cfg.Storage.Load(StorageKeys.SiteMeta(issuerKey, normalizedName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CertificateResource{}, err
|
return CertificateResource{}, err
|
||||||
}
|
}
|
||||||
|
@ -178,50 +256,6 @@ func (cfg *Config) loadCertResource(certNamesKey string) (CertificateResource, e
|
||||||
return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err)
|
return CertificateResource{}, fmt.Errorf("decoding certificate metadata: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: July 2020 - transition to new ACME lib and cert resource structure;
|
|
||||||
// for a while, we will need to convert old cert resources to new structure
|
|
||||||
certRes, err = cfg.transitionCertMetaToACMEzJuly2020Format(certRes, metaBytes)
|
|
||||||
if err != nil {
|
|
||||||
return certRes, fmt.Errorf("one-time certificate resource transition: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return certRes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this is a temporary transition helper starting July 2020.
|
|
||||||
// It can go away when we think enough time has passed that most active assets have transitioned.
|
|
||||||
func (cfg *Config) transitionCertMetaToACMEzJuly2020Format(certRes CertificateResource, metaBytes []byte) (CertificateResource, error) {
|
|
||||||
data, ok := certRes.IssuerData.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
return certRes, nil
|
|
||||||
}
|
|
||||||
if certURL, ok := data["url"].(string); ok && certURL != "" {
|
|
||||||
return certRes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldCertRes struct {
|
|
||||||
SANs []string `json:"sans"`
|
|
||||||
IssuerData struct {
|
|
||||||
Domain string `json:"domain"`
|
|
||||||
CertURL string `json:"certUrl"`
|
|
||||||
CertStableURL string `json:"certStableUrl"`
|
|
||||||
} `json:"issuer_data"`
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(metaBytes, &oldCertRes)
|
|
||||||
if err != nil {
|
|
||||||
return certRes, fmt.Errorf("decoding into old certificate resource type: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data = map[string]interface{}{
|
|
||||||
"url": oldCertRes.IssuerData.CertURL,
|
|
||||||
}
|
|
||||||
certRes.IssuerData = data
|
|
||||||
|
|
||||||
err = cfg.saveCertResource(certRes)
|
|
||||||
if err != nil {
|
|
||||||
return certRes, fmt.Errorf("saving converted certificate resource: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return certRes, nil
|
return certRes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,7 @@ func (fs *FileStorage) Lock(ctx context.Context, key string) error {
|
||||||
err2 := json.NewDecoder(f).Decode(&meta)
|
err2 := json.NewDecoder(f).Decode(&meta)
|
||||||
f.Close()
|
f.Close()
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
return fmt.Errorf("decoding lockfile contents: %w", err2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +306,15 @@ func updateLockfileFreshness(filename string) (bool, error) {
|
||||||
|
|
||||||
// write updated timestamp
|
// write updated timestamp
|
||||||
meta.Updated = time.Now()
|
meta.Updated = time.Now()
|
||||||
return false, json.NewEncoder(f).Encode(meta)
|
if err = json.NewEncoder(f).Encode(meta); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// sync to device; we suspect that sometimes file systems
|
||||||
|
// (particularly AWS EFS) don't do this on their own,
|
||||||
|
// leaving the file empty when we close it; see
|
||||||
|
// https://github.com/caddyserver/caddy/issues/3954
|
||||||
|
return false, f.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
// atomicallyCreateFile atomically creates the file
|
// atomicallyCreateFile atomically creates the file
|
||||||
|
@ -325,8 +333,11 @@ func atomicallyCreateFile(filename string, writeLockInfo bool) error {
|
||||||
Created: now,
|
Created: now,
|
||||||
Updated: now,
|
Updated: now,
|
||||||
}
|
}
|
||||||
err := json.NewEncoder(f).Encode(meta)
|
if err := json.NewEncoder(f).Encode(meta); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
|
}
|
||||||
|
// see https://github.com/caddyserver/caddy/issues/3954
|
||||||
|
if err := f.Sync(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/cpuid v1.2.5
|
github.com/klauspost/cpuid v1.2.5
|
||||||
github.com/libdns/libdns v0.1.0
|
github.com/libdns/libdns v0.2.0
|
||||||
github.com/mholt/acmez v0.1.1
|
github.com/mholt/acmez v0.1.3
|
||||||
github.com/miekg/dns v1.1.30
|
github.com/miekg/dns v1.1.30
|
||||||
go.uber.org/zap v1.15.0
|
go.uber.org/zap v1.15.0
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||||
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,10 +12,10 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/libdns/libdns v0.1.0 h1:0ctCOrVJsVzj53mop1angHp/pE3hmAhP7KiHvR0HD04=
|
github.com/libdns/libdns v0.2.0 h1:ewg3ByWrdUrxrje8ChPVMBNcotg7H9LQYg+u5De2RzI=
|
||||||
github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||||
github.com/mholt/acmez v0.1.1 h1:KQODCqk+hBn3O7qfCRPj6L96uG65T5BSS95FKNEqtdA=
|
github.com/mholt/acmez v0.1.3 h1:J7MmNIk4Qf9b8mAGqAh4XkNeowv3f1zW816yf4zt7Qk=
|
||||||
github.com/mholt/acmez v0.1.1/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
github.com/mholt/acmez v0.1.3/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
|
||||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
||||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
@ -47,9 +47,7 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
@ -57,7 +55,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEha
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -66,7 +63,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
|
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
|
|
@ -17,7 +17,6 @@ package certmagic
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -25,7 +24,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mholt/acmez"
|
"github.com/mholt/acmez"
|
||||||
"github.com/mholt/acmez/acme"
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,41 +42,23 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
|
||||||
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
|
// (https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05)
|
||||||
for _, proto := range clientHello.SupportedProtos {
|
for _, proto := range clientHello.SupportedProtos {
|
||||||
if proto == acmez.ACMETLS1Protocol {
|
if proto == acmez.ACMETLS1Protocol {
|
||||||
cfg.certCache.mu.RLock()
|
challengeCert, distributed, err := cfg.getTLSALPNChallengeCert(clientHello)
|
||||||
challengeCert, ok := cfg.certCache.cache[tlsALPNCertKeyName(clientHello.ServerName)]
|
if err != nil {
|
||||||
cfg.certCache.mu.RUnlock()
|
if cfg.Logger != nil {
|
||||||
if !ok {
|
cfg.Logger.Error("tls-alpn challenge",
|
||||||
// see if this challenge was started in a cluster; try distributed challenge solver
|
zap.String("server_name", clientHello.ServerName),
|
||||||
// (note that the tls.Config's ALPN settings must include the ACME TLS-ALPN challenge
|
zap.Error(err))
|
||||||
// protocol string, otherwise a valid certificate will not solve the challenge; we
|
|
||||||
// should already have taken care of that when we made the tls.Config)
|
|
||||||
challengeCert, ok, err := cfg.tryDistributedChallengeSolver(clientHello)
|
|
||||||
if err != nil {
|
|
||||||
if cfg.Logger != nil {
|
|
||||||
cfg.Logger.Error("tls-alpn challenge",
|
|
||||||
zap.String("server_name", clientHello.ServerName),
|
|
||||||
zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if ok {
|
return nil, err
|
||||||
if cfg.Logger != nil {
|
|
||||||
cfg.Logger.Info("served key authentication certificate",
|
|
||||||
zap.String("server_name", clientHello.ServerName),
|
|
||||||
zap.String("challenge", "tls-alpn-01"),
|
|
||||||
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
|
|
||||||
zap.Bool("distributed", true))
|
|
||||||
}
|
|
||||||
return &challengeCert.Certificate, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no certificate to complete TLS-ALPN challenge for SNI name: %s", clientHello.ServerName)
|
|
||||||
}
|
}
|
||||||
if cfg.Logger != nil {
|
if cfg.Logger != nil {
|
||||||
cfg.Logger.Info("served key authentication certificate",
|
cfg.Logger.Info("served key authentication certificate",
|
||||||
zap.String("server_name", clientHello.ServerName),
|
zap.String("server_name", clientHello.ServerName),
|
||||||
zap.String("challenge", "tls-alpn-01"),
|
zap.String("challenge", "tls-alpn-01"),
|
||||||
zap.String("remote", clientHello.Conn.RemoteAddr().String()))
|
zap.String("remote", clientHello.Conn.RemoteAddr().String()),
|
||||||
|
zap.Bool("distributed", distributed))
|
||||||
}
|
}
|
||||||
return &challengeCert.Certificate, nil
|
return challengeCert, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,16 +87,12 @@ func (cfg *Config) GetCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certif
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent use.
|
// This function is safe for concurrent use.
|
||||||
func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) {
|
func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate, matched, defaulted bool) {
|
||||||
name := NormalizedName(hello.ServerName)
|
name := normalizedName(hello.ServerName)
|
||||||
|
|
||||||
if name == "" {
|
if name == "" {
|
||||||
// if SNI is empty, prefer matching IP address
|
// if SNI is empty, prefer matching IP address
|
||||||
if hello.Conn != nil {
|
if hello.Conn != nil {
|
||||||
addr := hello.Conn.LocalAddr().String()
|
addr := localIPFromConn(hello.Conn)
|
||||||
ip, _, err := net.SplitHostPort(addr)
|
|
||||||
if err == nil {
|
|
||||||
addr = ip
|
|
||||||
}
|
|
||||||
cert, matched = cfg.selectCert(hello, addr)
|
cert, matched = cfg.selectCert(hello, addr)
|
||||||
if matched {
|
if matched {
|
||||||
return
|
return
|
||||||
|
@ -125,7 +101,7 @@ func (cfg *Config) getCertificate(hello *tls.ClientHelloInfo) (cert Certificate,
|
||||||
|
|
||||||
// fall back to a "default" certificate, if specified
|
// fall back to a "default" certificate, if specified
|
||||||
if cfg.DefaultServerName != "" {
|
if cfg.DefaultServerName != "" {
|
||||||
normDefault := NormalizedName(cfg.DefaultServerName)
|
normDefault := normalizedName(cfg.DefaultServerName)
|
||||||
cert, defaulted = cfg.selectCert(hello, normDefault)
|
cert, defaulted = cfg.selectCert(hello, normDefault)
|
||||||
if defaulted {
|
if defaulted {
|
||||||
return
|
return
|
||||||
|
@ -260,6 +236,12 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
|
||||||
if cfg.OnDemand != nil && loadIfNecessary {
|
if cfg.OnDemand != nil && loadIfNecessary {
|
||||||
// Then check to see if we have one on disk
|
// Then check to see if we have one on disk
|
||||||
loadedCert, err := cfg.CacheManagedCertificate(name)
|
loadedCert, err := cfg.CacheManagedCertificate(name)
|
||||||
|
if _, ok := err.(ErrNotExist); ok {
|
||||||
|
// If no exact match, try a wildcard variant, which is something we can still use
|
||||||
|
labels := strings.Split(name, ".")
|
||||||
|
labels[0] = "*"
|
||||||
|
loadedCert, err = cfg.CacheManagedCertificate(strings.Join(labels, "."))
|
||||||
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert)
|
loadedCert, err = cfg.handshakeMaintenance(hello, loadedCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -273,14 +255,6 @@ func (cfg *Config) getCertDuringHandshake(hello *tls.ClientHelloInfo, loadIfNece
|
||||||
}
|
}
|
||||||
if obtainIfNecessary {
|
if obtainIfNecessary {
|
||||||
// By this point, we need to ask the CA for a certificate
|
// By this point, we need to ask the CA for a certificate
|
||||||
|
|
||||||
// Make sure the certificate should be obtained based on config
|
|
||||||
err := cfg.checkIfCertShouldBeObtained(name)
|
|
||||||
if err != nil {
|
|
||||||
return Certificate{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain certificate from the CA
|
|
||||||
return cfg.obtainOnDemandCertificate(hello)
|
return cfg.obtainOnDemandCertificate(hello)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,6 +321,11 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif
|
||||||
|
|
||||||
name := cfg.getNameFromClientHello(hello)
|
name := cfg.getNameFromClientHello(hello)
|
||||||
|
|
||||||
|
getCertWithoutReobtaining := func() (Certificate, error) {
|
||||||
|
// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely
|
||||||
|
return cfg.getCertDuringHandshake(hello, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
// We must protect this process from happening concurrently, so synchronize.
|
// We must protect this process from happening concurrently, so synchronize.
|
||||||
obtainCertWaitChansMu.Lock()
|
obtainCertWaitChansMu.Lock()
|
||||||
wait, ok := obtainCertWaitChans[name]
|
wait, ok := obtainCertWaitChans[name]
|
||||||
|
@ -354,8 +333,17 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif
|
||||||
// lucky us -- another goroutine is already obtaining the certificate.
|
// lucky us -- another goroutine is already obtaining the certificate.
|
||||||
// wait for it to finish obtaining the cert and then we'll use it.
|
// wait for it to finish obtaining the cert and then we'll use it.
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
<-wait
|
|
||||||
return cfg.getCertDuringHandshake(hello, true, false)
|
// TODO: see if we can get a proper context in here, for true cancellation
|
||||||
|
timeout := time.NewTimer(2 * time.Minute)
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
return Certificate{}, fmt.Errorf("timed out waiting to obtain certificate for %s", name)
|
||||||
|
case <-wait:
|
||||||
|
timeout.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCertWithoutReobtaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
// looks like it's up to us to do all the work and obtain the cert.
|
// looks like it's up to us to do all the work and obtain the cert.
|
||||||
|
@ -364,22 +352,35 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif
|
||||||
obtainCertWaitChans[name] = wait
|
obtainCertWaitChans[name] = wait
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
|
||||||
// obtain the certificate
|
unblockWaiters := func() {
|
||||||
|
obtainCertWaitChansMu.Lock()
|
||||||
|
close(wait)
|
||||||
|
delete(obtainCertWaitChans, name)
|
||||||
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the certificate should be obtained based on config
|
||||||
|
err := cfg.checkIfCertShouldBeObtained(name)
|
||||||
|
if err != nil {
|
||||||
|
unblockWaiters()
|
||||||
|
return Certificate{}, err
|
||||||
|
}
|
||||||
|
|
||||||
if log != nil {
|
if log != nil {
|
||||||
log.Info("obtaining new certificate", zap.String("server_name", name))
|
log.Info("obtaining new certificate", zap.String("server_name", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false
|
// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false
|
||||||
ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second)
|
ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
err := cfg.ObtainCert(ctx, name, false)
|
|
||||||
|
// Obtain the certificate
|
||||||
|
err = cfg.ObtainCert(ctx, name, false)
|
||||||
|
|
||||||
// immediately unblock anyone waiting for it; doing this in
|
// immediately unblock anyone waiting for it; doing this in
|
||||||
// a defer would risk deadlock because of the recursive call
|
// a defer would risk deadlock because of the recursive call
|
||||||
// to getCertDuringHandshake below when we return!
|
// to getCertDuringHandshake below when we return!
|
||||||
obtainCertWaitChansMu.Lock()
|
unblockWaiters()
|
||||||
close(wait)
|
|
||||||
delete(obtainCertWaitChans, name)
|
|
||||||
obtainCertWaitChansMu.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// shucks; failed to solve challenge on-demand
|
// shucks; failed to solve challenge on-demand
|
||||||
|
@ -388,7 +389,7 @@ func (cfg *Config) obtainOnDemandCertificate(hello *tls.ClientHelloInfo) (Certif
|
||||||
|
|
||||||
// success; certificate was just placed on disk, so
|
// success; certificate was just placed on disk, so
|
||||||
// we need only restart serving the certificate
|
// we need only restart serving the certificate
|
||||||
return cfg.getCertDuringHandshake(hello, true, false)
|
return getCertWithoutReobtaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
// handshakeMaintenance performs a check on cert for expiration and OCSP validity.
|
// handshakeMaintenance performs a check on cert for expiration and OCSP validity.
|
||||||
|
@ -400,13 +401,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi
|
||||||
log := loggerNamed(cfg.Logger, "on_demand")
|
log := loggerNamed(cfg.Logger, "on_demand")
|
||||||
|
|
||||||
// Check cert expiration
|
// Check cert expiration
|
||||||
timeLeft := cert.Leaf.NotAfter.Sub(time.Now().UTC())
|
|
||||||
if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) {
|
if currentlyInRenewalWindow(cert.Leaf.NotBefore, cert.Leaf.NotAfter, cfg.RenewalWindowRatio) {
|
||||||
if log != nil {
|
|
||||||
log.Info("certificate expires soon; attempting renewal",
|
|
||||||
zap.Strings("identifiers", cert.Names),
|
|
||||||
zap.Duration("remaining", timeLeft))
|
|
||||||
}
|
|
||||||
return cfg.renewDynamicCertificate(hello, cert)
|
return cfg.renewDynamicCertificate(hello, cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +409,7 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi
|
||||||
if cert.ocsp != nil {
|
if cert.ocsp != nil {
|
||||||
refreshTime := cert.ocsp.ThisUpdate.Add(cert.ocsp.NextUpdate.Sub(cert.ocsp.ThisUpdate) / 2)
|
refreshTime := cert.ocsp.ThisUpdate.Add(cert.ocsp.NextUpdate.Sub(cert.ocsp.ThisUpdate) / 2)
|
||||||
if time.Now().After(refreshTime) {
|
if time.Now().After(refreshTime) {
|
||||||
_, err := stapleOCSP(cfg.Storage, &cert, nil)
|
_, err := stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// An error with OCSP stapling is not the end of the world, and in fact, is
|
// An error with OCSP stapling is not the end of the world, and in fact, is
|
||||||
// quite common considering not all certs have issuer URLs that support it.
|
// quite common considering not all certs have issuer URLs that support it.
|
||||||
|
@ -436,22 +431,59 @@ func (cfg *Config) handshakeMaintenance(hello *tls.ClientHelloInfo, cert Certifi
|
||||||
// renewDynamicCertificate renews the certificate for name using cfg. It returns the
|
// renewDynamicCertificate renews the certificate for name using cfg. It returns the
|
||||||
// certificate to use and an error, if any. name should already be lower-cased before
|
// certificate to use and an error, if any. name should already be lower-cased before
|
||||||
// calling this function. name is the name obtained directly from the handshake's
|
// calling this function. name is the name obtained directly from the handshake's
|
||||||
// ClientHello.
|
// ClientHello. If the certificate hasn't yet expired, currentCert will be returned
|
||||||
|
// and the renewal will happen in the background; otherwise this blocks until the
|
||||||
|
// certificate has been renewed, and returns the renewed certificate.
|
||||||
//
|
//
|
||||||
// This function is safe for use by multiple concurrent goroutines.
|
// This function is safe for use by multiple concurrent goroutines.
|
||||||
func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) {
|
func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCert Certificate) (Certificate, error) {
|
||||||
log := loggerNamed(cfg.Logger, "on_demand")
|
log := loggerNamed(cfg.Logger, "on_demand")
|
||||||
|
|
||||||
name := cfg.getNameFromClientHello(hello)
|
name := cfg.getNameFromClientHello(hello)
|
||||||
|
timeLeft := time.Until(currentCert.Leaf.NotAfter)
|
||||||
|
|
||||||
|
getCertWithoutReobtaining := func() (Certificate, error) {
|
||||||
|
// very important to set the obtainIfNecessary argument to false, so we don't repeat this infinitely
|
||||||
|
return cfg.getCertDuringHandshake(hello, true, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if another goroutine is already working on this certificate
|
||||||
obtainCertWaitChansMu.Lock()
|
obtainCertWaitChansMu.Lock()
|
||||||
wait, ok := obtainCertWaitChans[name]
|
wait, ok := obtainCertWaitChans[name]
|
||||||
if ok {
|
if ok {
|
||||||
// lucky us -- another goroutine is already renewing the certificate.
|
// lucky us -- another goroutine is already renewing the certificate
|
||||||
// wait for it to finish, then we'll use the new one.
|
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
<-wait
|
|
||||||
return cfg.getCertDuringHandshake(hello, true, false)
|
if timeLeft > 0 {
|
||||||
|
// the current certificate hasn't expired, and another goroutine is already
|
||||||
|
// renewing it, so we might as well serve what we have without blocking
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("certificate expires soon but is already being renewed; serving current certificate",
|
||||||
|
zap.Strings("identifiers", currentCert.Names),
|
||||||
|
zap.Duration("remaining", timeLeft))
|
||||||
|
}
|
||||||
|
return currentCert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, we'll have to wait for the renewal to finish so we don't serve
|
||||||
|
// an expired certificate
|
||||||
|
|
||||||
|
if log != nil {
|
||||||
|
log.Debug("certificate has expired, but is already being renewed; waiting for renewal to complete",
|
||||||
|
zap.Strings("identifiers", currentCert.Names),
|
||||||
|
zap.Time("expired", currentCert.Leaf.NotAfter))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: see if we can get a proper context in here, for true cancellation
|
||||||
|
timeout := time.NewTimer(2 * time.Minute)
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
return Certificate{}, fmt.Errorf("timed out waiting for certificate renewal of %s", name)
|
||||||
|
case <-wait:
|
||||||
|
timeout.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCertWithoutReobtaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
// looks like it's up to us to do all the work and renew the cert
|
// looks like it's up to us to do all the work and renew the cert
|
||||||
|
@ -459,6 +491,21 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||||
obtainCertWaitChans[name] = wait
|
obtainCertWaitChans[name] = wait
|
||||||
obtainCertWaitChansMu.Unlock()
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
|
||||||
|
unblockWaiters := func() {
|
||||||
|
obtainCertWaitChansMu.Lock()
|
||||||
|
close(wait)
|
||||||
|
delete(obtainCertWaitChans, name)
|
||||||
|
obtainCertWaitChansMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
if log != nil {
|
||||||
|
log.Info("attempting certificate renewal",
|
||||||
|
zap.String("server_name", name),
|
||||||
|
zap.Strings("identifiers", currentCert.Names),
|
||||||
|
zap.Time("expiration", currentCert.Leaf.NotAfter),
|
||||||
|
zap.Duration("remaining", timeLeft))
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure a certificate for this name should be obtained on-demand
|
// Make sure a certificate for this name should be obtained on-demand
|
||||||
err := cfg.checkIfCertShouldBeObtained(name)
|
err := cfg.checkIfCertShouldBeObtained(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -466,105 +513,118 @@ func (cfg *Config) renewDynamicCertificate(hello *tls.ClientHelloInfo, currentCe
|
||||||
cfg.certCache.mu.Lock()
|
cfg.certCache.mu.Lock()
|
||||||
cfg.certCache.removeCertificate(currentCert)
|
cfg.certCache.removeCertificate(currentCert)
|
||||||
cfg.certCache.mu.Unlock()
|
cfg.certCache.mu.Unlock()
|
||||||
|
unblockWaiters()
|
||||||
return Certificate{}, err
|
return Certificate{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// renew and reload the certificate
|
// Renew and reload the certificate
|
||||||
if log != nil {
|
renewAndReload := func(ctx context.Context, cancel context.CancelFunc) (Certificate, error) {
|
||||||
log.Info("renewing certificate", zap.String("server_name", name))
|
defer cancel()
|
||||||
}
|
err = cfg.RenewCert(ctx, name, false)
|
||||||
// TODO: use a proper context; we use one with timeout because retries are enabled because interactive is false
|
if err == nil {
|
||||||
ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second)
|
// even though the recursive nature of the dynamic cert loading
|
||||||
defer cancel()
|
// would just call this function anyway, we do it here to
|
||||||
err = cfg.RenewCert(ctx, name, false)
|
// make the replacement as atomic as possible.
|
||||||
if err == nil {
|
newCert, err := cfg.CacheManagedCertificate(name)
|
||||||
// even though the recursive nature of the dynamic cert loading
|
if err != nil {
|
||||||
// would just call this function anyway, we do it here to
|
if log != nil {
|
||||||
// make the replacement as atomic as possible.
|
log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err))
|
||||||
newCert, err := cfg.CacheManagedCertificate(name)
|
}
|
||||||
if err != nil {
|
} else {
|
||||||
if log != nil {
|
// replace the old certificate with the new one
|
||||||
log.Error("loading renewed certificate", zap.String("server_name", name), zap.Error(err))
|
cfg.certCache.replaceCertificate(currentCert, newCert)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// replace the old certificate with the new one
|
|
||||||
cfg.certCache.replaceCertificate(currentCert, newCert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// immediately unblock anyone waiting for it; doing this in
|
||||||
|
// a defer would risk deadlock because of the recursive call
|
||||||
|
// to getCertDuringHandshake below when we return!
|
||||||
|
unblockWaiters()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return Certificate{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return getCertWithoutReobtaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
// immediately unblock anyone waiting for it; doing this in
|
// if the certificate hasn't expired, we can serve what we have and renew in the background
|
||||||
// a defer would risk deadlock because of the recursive call
|
if timeLeft > 0 {
|
||||||
// to getCertDuringHandshake below when we return!
|
// TODO: get a proper context; we use one with timeout because retries are enabled because interactive is false
|
||||||
obtainCertWaitChansMu.Lock()
|
ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Minute)
|
||||||
close(wait)
|
go renewAndReload(ctx, cancel)
|
||||||
delete(obtainCertWaitChans, name)
|
return currentCert, nil
|
||||||
obtainCertWaitChansMu.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Certificate{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cfg.getCertDuringHandshake(hello, true, false)
|
// otherwise, we have to block while we renew an expired certificate
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 90*time.Second)
|
||||||
|
return renewAndReload(ctx, cancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tryDistributedChallengeSolver is to be called when the clientHello pertains to
|
// getTLSALPNChallengeCert is to be called when the clientHello pertains to
|
||||||
// a TLS-ALPN challenge and a certificate is required to solve it. This method
|
// a TLS-ALPN challenge and a certificate is required to solve it. This method gets
|
||||||
// checks the distributed store of challenge info files and, if a matching ServerName
|
// the relevant challenge info and then returns the associated certificate (if any)
|
||||||
// is present, it makes a certificate to solve this challenge and returns it. For
|
// or generates it anew if it's not available (as is the case when distributed
|
||||||
// this to succeed, it requires that cfg.Issuer is of type *ACMEManager.
|
// solving). True is returned if the challenge is being solved distributed (there
|
||||||
// A boolean true is returned if a valid certificate is returned.
|
// is no semantic difference with distributed solving; it is mainly for logging).
|
||||||
func (cfg *Config) tryDistributedChallengeSolver(clientHello *tls.ClientHelloInfo) (Certificate, bool, error) {
|
func (cfg *Config) getTLSALPNChallengeCert(clientHello *tls.ClientHelloInfo) (*tls.Certificate, bool, error) {
|
||||||
am, ok := cfg.Issuer.(*ACMEManager)
|
chalData, distributed, err := cfg.getChallengeInfo(clientHello.ServerName)
|
||||||
if !ok {
|
|
||||||
return Certificate{}, false, nil
|
|
||||||
}
|
|
||||||
tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(clientHello.ServerName)
|
|
||||||
chalInfoBytes, err := cfg.Storage.Load(tokenKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(ErrNotExist); ok {
|
return nil, distributed, err
|
||||||
return Certificate{}, false, nil
|
|
||||||
}
|
|
||||||
return Certificate{}, false, fmt.Errorf("opening distributed challenge token file %s: %v", tokenKey, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var chalInfo acme.Challenge
|
// fast path: we already created the certificate (this avoids having to re-create
|
||||||
err = json.Unmarshal(chalInfoBytes, &chalInfo)
|
// it at every handshake that tries to verify, e.g. multi-perspective validation)
|
||||||
if err != nil {
|
if chalData.data != nil {
|
||||||
return Certificate{}, false, fmt.Errorf("decoding challenge token file %s (corrupted?): %v", tokenKey, err)
|
return chalData.data.(*tls.Certificate), distributed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cert, err := acmez.TLSALPN01ChallengeCert(chalInfo)
|
// otherwise, we can re-create the solution certificate, but it takes a few cycles
|
||||||
|
cert, err := acmez.TLSALPN01ChallengeCert(chalData.Challenge)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Certificate{}, false, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err)
|
return nil, distributed, fmt.Errorf("making TLS-ALPN challenge certificate: %v", err)
|
||||||
}
|
}
|
||||||
if cert == nil {
|
if cert == nil {
|
||||||
return Certificate{}, false, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error")
|
return nil, distributed, fmt.Errorf("got nil TLS-ALPN challenge certificate but no error")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Certificate{Certificate: *cert}, true, nil
|
return cert, distributed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNameFromClientHello returns a normalized form of hello.ServerName.
|
// getNameFromClientHello returns a normalized form of hello.ServerName.
|
||||||
// If hello.ServerName is empty (i.e. client did not use SNI), then the
|
// If hello.ServerName is empty (i.e. client did not use SNI), then the
|
||||||
// associated connection's local address is used to extract an IP address.
|
// associated connection's local address is used to extract an IP address.
|
||||||
func (*Config) getNameFromClientHello(hello *tls.ClientHelloInfo) string {
|
func (*Config) getNameFromClientHello(hello *tls.ClientHelloInfo) string {
|
||||||
name := NormalizedName(hello.ServerName)
|
if name := normalizedName(hello.ServerName); name != "" {
|
||||||
if name != "" || hello.Conn == nil {
|
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
return localIPFromConn(hello.Conn)
|
||||||
// if no SNI, try using IP address on the connection
|
|
||||||
localAddr := hello.Conn.LocalAddr().String()
|
|
||||||
localAddrHost, _, err := net.SplitHostPort(localAddr)
|
|
||||||
if err == nil {
|
|
||||||
return localAddrHost
|
|
||||||
}
|
|
||||||
return localAddr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NormalizedName returns a cleaned form of serverName that is
|
// localIPFromConn returns the host portion of c's local address
|
||||||
|
// and strips the scope ID if one exists (see RFC 4007).
|
||||||
|
func localIPFromConn(c net.Conn) string {
|
||||||
|
if c == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
localAddr := c.LocalAddr().String()
|
||||||
|
ip, _, err := net.SplitHostPort(localAddr)
|
||||||
|
if err != nil {
|
||||||
|
// OK; assume there was no port
|
||||||
|
ip = localAddr
|
||||||
|
}
|
||||||
|
// IPv6 addresses can have scope IDs, e.g. "fe80::4c3:3cff:fe4f:7e0b%eth0",
|
||||||
|
// but for our purposes, these are useless (unless a valid use case proves
|
||||||
|
// otherwise; see issue #3911)
|
||||||
|
if scopeIDStart := strings.Index(ip, "%"); scopeIDStart > -1 {
|
||||||
|
ip = ip[:scopeIDStart]
|
||||||
|
}
|
||||||
|
return ip
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizedName returns a cleaned form of serverName that is
|
||||||
// used for consistency when referring to a SNI value.
|
// used for consistency when referring to a SNI value.
|
||||||
func NormalizedName(serverName string) string {
|
func normalizedName(serverName string) string {
|
||||||
return strings.ToLower(strings.TrimSpace(serverName))
|
return strings.ToLower(strings.TrimSpace(serverName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
package certmagic
|
package certmagic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -71,41 +70,24 @@ func (am *ACMEManager) distributedHTTPChallengeSolver(w http.ResponseWriter, r *
|
||||||
if am == nil {
|
if am == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
host := hostOnly(r.Host)
|
host := hostOnly(r.Host)
|
||||||
|
chalInfo, distributed, err := am.config.getChallengeInfo(host)
|
||||||
tokenKey := distributedSolver{acmeManager: am, caURL: am.CA}.challengeTokensKey(host)
|
|
||||||
chalInfoBytes, err := am.config.Storage.Load(tokenKey)
|
|
||||||
if err != nil {
|
|
||||||
if _, ok := err.(ErrNotExist); !ok {
|
|
||||||
if am.Logger != nil {
|
|
||||||
am.Logger.Error("opening distributed HTTP challenge token file",
|
|
||||||
zap.String("host", host),
|
|
||||||
zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var challenge acme.Challenge
|
|
||||||
err = json.Unmarshal(chalInfoBytes, &challenge)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if am.Logger != nil {
|
if am.Logger != nil {
|
||||||
am.Logger.Error("decoding HTTP challenge token file (corrupted?)",
|
am.Logger.Error("looking up info for HTTP challenge",
|
||||||
zap.String("host", host),
|
zap.String("host", host),
|
||||||
zap.String("token_key", tokenKey),
|
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return solveHTTPChallenge(am.Logger, w, r, chalInfo.Challenge, distributed)
|
||||||
return am.answerHTTPChallenge(w, r, challenge)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// answerHTTPChallenge solves the challenge with chalInfo.
|
// solveHTTPChallenge solves the HTTP challenge using the given challenge information.
|
||||||
// Most of this code borrowed from xenolf's built-in HTTP-01
|
// If the challenge is being solved in a distributed fahsion, set distributed to true for logging purposes.
|
||||||
// challenge solver in March 2018.
|
// It returns true the properties of the request check out in relation to the HTTP challenge.
|
||||||
func (am *ACMEManager) answerHTTPChallenge(w http.ResponseWriter, r *http.Request, challenge acme.Challenge) bool {
|
// Most of this code borrowed from xenolf's built-in HTTP-01 challenge solver in March 2018.
|
||||||
|
func solveHTTPChallenge(logger *zap.Logger, w http.ResponseWriter, r *http.Request, challenge acme.Challenge, distributed bool) bool {
|
||||||
challengeReqPath := challenge.HTTP01ResourcePath()
|
challengeReqPath := challenge.HTTP01ResourcePath()
|
||||||
if r.URL.Path == challengeReqPath &&
|
if r.URL.Path == challengeReqPath &&
|
||||||
strings.EqualFold(hostOnly(r.Host), challenge.Identifier.Value) && // mitigate DNS rebinding attacks
|
strings.EqualFold(hostOnly(r.Host), challenge.Identifier.Value) && // mitigate DNS rebinding attacks
|
||||||
|
@ -113,17 +95,26 @@ func (am *ACMEManager) answerHTTPChallenge(w http.ResponseWriter, r *http.Reques
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
w.Write([]byte(challenge.KeyAuthorization))
|
w.Write([]byte(challenge.KeyAuthorization))
|
||||||
r.Close = true
|
r.Close = true
|
||||||
if am.Logger != nil {
|
if logger != nil {
|
||||||
am.Logger.Info("served key authentication",
|
logger.Info("served key authentication",
|
||||||
zap.String("identifier", challenge.Identifier.Value),
|
zap.String("identifier", challenge.Identifier.Value),
|
||||||
zap.String("challenge", "http-01"),
|
zap.String("challenge", "http-01"),
|
||||||
zap.String("remote", r.RemoteAddr))
|
zap.String("remote", r.RemoteAddr),
|
||||||
|
zap.Bool("distributed", distributed))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SolveHTTPChallenge solves the HTTP challenge. It should be used only on HTTP requests that are
|
||||||
|
// from ACME servers trying to validate an identifier (i.e. LooksLikeHTTPChallenge() == true). It
|
||||||
|
// returns true if the request criteria check out and it answered with key authentication, in which
|
||||||
|
// case no further handling of the request is necessary.
|
||||||
|
func SolveHTTPChallenge(logger *zap.Logger, w http.ResponseWriter, r *http.Request, challenge acme.Challenge) bool {
|
||||||
|
return solveHTTPChallenge(logger, w, r, challenge, false)
|
||||||
|
}
|
||||||
|
|
||||||
// LooksLikeHTTPChallenge returns true if r looks like an ACME
|
// LooksLikeHTTPChallenge returns true if r looks like an ACME
|
||||||
// HTTP challenge request from an ACME server.
|
// HTTP challenge request from an ACME server.
|
||||||
func LooksLikeHTTPChallenge(r *http.Request) bool {
|
func LooksLikeHTTPChallenge(r *http.Request) bool {
|
||||||
|
|
|
@ -141,6 +141,9 @@ func (certCache *Cache) RenewManagedCertificates(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if cfg.OnDemand != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// if time is up or expires soon, we need to try to renew it
|
// if time is up or expires soon, we need to try to renew it
|
||||||
if cert.NeedsRenewal(cfg) {
|
if cert.NeedsRenewal(cfg) {
|
||||||
|
@ -337,8 +340,8 @@ func (certCache *Cache) updateOCSPStaples(ctx context.Context) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ocspResp, err := stapleOCSP(cfg.Storage, &cert, nil)
|
ocspResp, err := stapleOCSP(cfg.OCSP, cfg.Storage, &cert, nil)
|
||||||
if err != nil {
|
if err != nil || ocspResp == nil {
|
||||||
if cert.ocsp != nil {
|
if cert.ocsp != nil {
|
||||||
// if there was no staple before, that's fine; otherwise we should log the error
|
// if there was no staple before, that's fine; otherwise we should log the error
|
||||||
if log != nil {
|
if log != nil {
|
||||||
|
|
|
@ -34,11 +34,16 @@ import (
|
||||||
// If you don't have the PEM blocks already, just pass in nil.
|
// If you don't have the PEM blocks already, just pass in nil.
|
||||||
//
|
//
|
||||||
// Errors here are not necessarily fatal, it could just be that the
|
// Errors here are not necessarily fatal, it could just be that the
|
||||||
// certificate doesn't have an issuer URL.
|
// certificate doesn't have an issuer URL. This function may return
|
||||||
|
// both nil values if OCSP stapling is disabled according to ocspConfig.
|
||||||
//
|
//
|
||||||
// If a status was received, it returns that status. Note that the
|
// If a status was received, it returns that status. Note that the
|
||||||
// returned status is not always stapled to the certificate.
|
// returned status is not always stapled to the certificate.
|
||||||
func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Response, error) {
|
func stapleOCSP(ocspConfig OCSPConfig, storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Response, error) {
|
||||||
|
if ocspConfig.DisableStapling {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
if pemBundle == nil {
|
if pemBundle == nil {
|
||||||
// we need a PEM encoding only for some function calls below
|
// we need a PEM encoding only for some function calls below
|
||||||
bundle := new(bytes.Buffer)
|
bundle := new(bytes.Buffer)
|
||||||
|
@ -82,7 +87,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Res
|
||||||
// If we couldn't get a fresh staple by reading the cache,
|
// If we couldn't get a fresh staple by reading the cache,
|
||||||
// then we need to request it from the OCSP responder
|
// then we need to request it from the OCSP responder
|
||||||
if ocspResp == nil || len(ocspBytes) == 0 {
|
if ocspResp == nil || len(ocspBytes) == 0 {
|
||||||
ocspBytes, ocspResp, ocspErr = getOCSPForCert(pemBundle)
|
ocspBytes, ocspResp, ocspErr = getOCSPForCert(ocspConfig, pemBundle)
|
||||||
if ocspErr != nil {
|
if ocspErr != nil {
|
||||||
// An error here is not a problem because a certificate may simply
|
// An error here is not a problem because a certificate may simply
|
||||||
// not contain a link to an OCSP server. But we should log it anyway.
|
// not contain a link to an OCSP server. But we should log it anyway.
|
||||||
|
@ -125,7 +130,7 @@ func stapleOCSP(storage Storage, cert *Certificate, pemBundle []byte) (*ocsp.Res
|
||||||
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
||||||
//
|
//
|
||||||
// Borrowed from xenolf.
|
// Borrowed from xenolf.
|
||||||
func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
func getOCSPForCert(ocspConfig OCSPConfig, bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||||
// TODO: Perhaps this should be synchronized too, with a Locker?
|
// TODO: Perhaps this should be synchronized too, with a Locker?
|
||||||
|
|
||||||
certificates, err := parseCertsFromPEMBundle(bundle)
|
certificates, err := parseCertsFromPEMBundle(bundle)
|
||||||
|
@ -142,6 +147,18 @@ func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||||
if len(issuedCert.OCSPServer) == 0 {
|
if len(issuedCert.OCSPServer) == 0 {
|
||||||
return nil, nil, fmt.Errorf("no OCSP server specified in certificate")
|
return nil, nil, fmt.Errorf("no OCSP server specified in certificate")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply override for responder URL
|
||||||
|
respURL := issuedCert.OCSPServer[0]
|
||||||
|
if len(ocspConfig.ResponderOverrides) > 0 {
|
||||||
|
if override, ok := ocspConfig.ResponderOverrides[respURL]; ok {
|
||||||
|
respURL = override
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if respURL == "" {
|
||||||
|
return nil, nil, fmt.Errorf("override disables querying OCSP responder: %v", issuedCert.OCSPServer[0])
|
||||||
|
}
|
||||||
|
|
||||||
if len(certificates) == 1 {
|
if len(certificates) == 1 {
|
||||||
if len(issuedCert.IssuingCertificateURL) == 0 {
|
if len(issuedCert.IssuingCertificateURL) == 0 {
|
||||||
return nil, nil, fmt.Errorf("no URL to issuing certificate")
|
return nil, nil, fmt.Errorf("no URL to issuing certificate")
|
||||||
|
@ -176,7 +193,7 @@ func getOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bytes.NewReader(ocspReq)
|
reader := bytes.NewReader(ocspReq)
|
||||||
req, err := http.Post(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
|
req, err := http.Post(respURL, "application/ocsp-request", reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("making OCSP request: %v", err)
|
return nil, nil, fmt.Errorf("making OCSP request: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,22 +123,19 @@ type tlsALPNSolver struct {
|
||||||
// Present adds the certificate to the certificate cache and, if
|
// Present adds the certificate to the certificate cache and, if
|
||||||
// needed, starts a TLS server for answering TLS-ALPN challenges.
|
// needed, starts a TLS server for answering TLS-ALPN challenges.
|
||||||
func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error {
|
func (s *tlsALPNSolver) Present(ctx context.Context, chal acme.Challenge) error {
|
||||||
// load the certificate into the cache; this isn't strictly necessary
|
// we pre-generate the certificate for efficiency with multi-perspective
|
||||||
// if we're using the distributed solver since our GetCertificate
|
// validation, so it only has to be done once (at least, by this instance;
|
||||||
// function will check storage for the keyAuth anyway, but it seems
|
// distributed solving does not have that luxury, oh well) - update the
|
||||||
// like loading it into the cache is the right thing to do
|
// challenge data in memory to be the generated certificate
|
||||||
cert, err := acmez.TLSALPN01ChallengeCert(chal)
|
cert, err := acmez.TLSALPN01ChallengeCert(chal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
certHash := hashCertificateChain(cert.Certificate)
|
activeChallengesMu.Lock()
|
||||||
s.config.certCache.mu.Lock()
|
chalData := activeChallenges[chal.Identifier.Value]
|
||||||
s.config.certCache.cache[tlsALPNCertKeyName(chal.Identifier.Value)] = Certificate{
|
chalData.data = cert
|
||||||
Certificate: *cert,
|
activeChallenges[chal.Identifier.Value] = chalData
|
||||||
Names: []string{chal.Identifier.Value},
|
activeChallengesMu.Unlock()
|
||||||
hash: certHash, // perhaps not necesssary
|
|
||||||
}
|
|
||||||
s.config.certCache.mu.Unlock()
|
|
||||||
|
|
||||||
// the rest of this function increments the
|
// the rest of this function increments the
|
||||||
// challenge count for the solver at this
|
// challenge count for the solver at this
|
||||||
|
@ -273,13 +270,6 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err
|
||||||
dnsName := challenge.DNS01TXTRecordName()
|
dnsName := challenge.DNS01TXTRecordName()
|
||||||
keyAuth := challenge.DNS01KeyAuthorization()
|
keyAuth := challenge.DNS01KeyAuthorization()
|
||||||
|
|
||||||
rec := libdns.Record{
|
|
||||||
Type: "TXT",
|
|
||||||
Name: dnsName,
|
|
||||||
Value: keyAuth,
|
|
||||||
TTL: s.TTL,
|
|
||||||
}
|
|
||||||
|
|
||||||
// multiple identifiers can have the same ACME challenge
|
// multiple identifiers can have the same ACME challenge
|
||||||
// domain (e.g. example.com and *.example.com) so we need
|
// domain (e.g. example.com and *.example.com) so we need
|
||||||
// to ensure that we don't solve those concurrently and
|
// to ensure that we don't solve those concurrently and
|
||||||
|
@ -292,6 +282,13 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err
|
||||||
return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err)
|
return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rec := libdns.Record{
|
||||||
|
Type: "TXT",
|
||||||
|
Name: libdns.RelativeName(dnsName+".", zone),
|
||||||
|
Value: keyAuth,
|
||||||
|
TTL: s.TTL,
|
||||||
|
}
|
||||||
|
|
||||||
results, err := s.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec})
|
results, err := s.DNSProvider.AppendRecords(ctx, zone, []libdns.Record{rec})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("adding temporary record for zone %s: %w", zone, err)
|
return fmt.Errorf("adding temporary record for zone %s: %w", zone, err)
|
||||||
|
@ -458,20 +455,19 @@ func (mmu *mapMutex) locked(key interface{}) (ok bool) {
|
||||||
// sharing sync and storage, and using the facilities provided by
|
// sharing sync and storage, and using the facilities provided by
|
||||||
// this package for solving the challenges.
|
// this package for solving the challenges.
|
||||||
type distributedSolver struct {
|
type distributedSolver struct {
|
||||||
// The config with a certificate cache
|
// The storage backing the distributed solver. It must be
|
||||||
// with a reference to the storage to
|
// the same storage configuration as what is solving the
|
||||||
// use which is shared among all the
|
// challenge in order to be effective.
|
||||||
// instances in the cluster - REQUIRED.
|
storage Storage
|
||||||
acmeManager *ACMEManager
|
|
||||||
|
// The storage key prefix, associated with the issuer
|
||||||
|
// that is solving the challenge.
|
||||||
|
storageKeyIssuerPrefix string
|
||||||
|
|
||||||
// Since the distributedSolver is only a
|
// Since the distributedSolver is only a
|
||||||
// wrapper over an actual solver, place
|
// wrapper over an actual solver, place
|
||||||
// the actual solver here.
|
// the actual solver here.
|
||||||
solver acmez.Solver
|
solver acmez.Solver
|
||||||
|
|
||||||
// The CA endpoint URL associated with
|
|
||||||
// this solver.
|
|
||||||
caURL string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present invokes the underlying solver's Present method
|
// Present invokes the underlying solver's Present method
|
||||||
|
@ -483,7 +479,7 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = dhs.acmeManager.config.Storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes)
|
err = dhs.storage.Store(dhs.challengeTokensKey(chal.Identifier.Value), infoBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -495,10 +491,18 @@ func (dhs distributedSolver) Present(ctx context.Context, chal acme.Challenge) e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait wraps the underlying solver's Wait() method, if any. Implements acmez.Waiter.
|
||||||
|
func (dhs distributedSolver) Wait(ctx context.Context, challenge acme.Challenge) error {
|
||||||
|
if waiter, ok := dhs.solver.(acmez.Waiter); ok {
|
||||||
|
return waiter.Wait(ctx, challenge)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CleanUp invokes the underlying solver's CleanUp method
|
// CleanUp invokes the underlying solver's CleanUp method
|
||||||
// and also cleans up any assets saved to storage.
|
// and also cleans up any assets saved to storage.
|
||||||
func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
||||||
err := dhs.acmeManager.config.Storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value))
|
err := dhs.storage.Delete(dhs.challengeTokensKey(chal.Identifier.Value))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -511,7 +515,7 @@ func (dhs distributedSolver) CleanUp(ctx context.Context, chal acme.Challenge) e
|
||||||
|
|
||||||
// challengeTokensPrefix returns the key prefix for challenge info.
|
// challengeTokensPrefix returns the key prefix for challenge info.
|
||||||
func (dhs distributedSolver) challengeTokensPrefix() string {
|
func (dhs distributedSolver) challengeTokensPrefix() string {
|
||||||
return path.Join(dhs.acmeManager.storageKeyCAPrefix(dhs.caURL), "challenge_tokens")
|
return path.Join(dhs.storageKeyIssuerPrefix, "challenge_tokens")
|
||||||
}
|
}
|
||||||
|
|
||||||
// challengeTokensKey returns the key to use to store and access
|
// challengeTokensKey returns the key to use to store and access
|
||||||
|
@ -607,6 +611,15 @@ func dialTCPSocket(addr string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetACMEChallenge returns an active ACME challenge for the given identifier,
|
||||||
|
// or false if no active challenge for that identifier is known.
|
||||||
|
func GetACMEChallenge(identifier string) (Challenge, bool) {
|
||||||
|
activeChallengesMu.Lock()
|
||||||
|
chalData, ok := activeChallenges[identifier]
|
||||||
|
activeChallengesMu.Unlock()
|
||||||
|
return chalData, ok
|
||||||
|
}
|
||||||
|
|
||||||
// The active challenge solvers, keyed by listener address,
|
// The active challenge solvers, keyed by listener address,
|
||||||
// and protected by a mutex. Note that the creation of
|
// and protected by a mutex. Note that the creation of
|
||||||
// solver listeners and the incrementing of their counts
|
// solver listeners and the incrementing of their counts
|
||||||
|
@ -616,8 +629,56 @@ var (
|
||||||
solversMu sync.Mutex
|
solversMu sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// activeChallenges holds information about all known, currently-active
|
||||||
|
// ACME challenges, keyed by identifier. CertMagic guarantees that
|
||||||
|
// challenges for the same identifier do not overlap, by its locking
|
||||||
|
// mechanisms; thus if a challenge comes in for a certain identifier,
|
||||||
|
// we can be confident that if this process initiated the challenge,
|
||||||
|
// the correct information to solve it is in this map. (It may have
|
||||||
|
// alternatively been initiated by another instance in a cluster, in
|
||||||
|
// which case the distributed solver will take care of that.)
|
||||||
|
var (
|
||||||
|
activeChallenges = make(map[string]Challenge)
|
||||||
|
activeChallengesMu sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// Challenge is an ACME challenge, but optionally paired with
|
||||||
|
// data that can make it easier or more efficient to solve.
|
||||||
|
type Challenge struct {
|
||||||
|
acme.Challenge
|
||||||
|
data interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// solverWrapper should be used to wrap all challenge solvers so that
|
||||||
|
// we can add the challenge info to memory; this makes challenges globally
|
||||||
|
// solvable by a single HTTP or TLS server even if multiple servers with
|
||||||
|
// different configurations/scopes need to get certificates.
|
||||||
|
type solverWrapper struct{ acmez.Solver }
|
||||||
|
|
||||||
|
func (sw solverWrapper) Present(ctx context.Context, chal acme.Challenge) error {
|
||||||
|
activeChallengesMu.Lock()
|
||||||
|
activeChallenges[chal.Identifier.Value] = Challenge{Challenge: chal}
|
||||||
|
activeChallengesMu.Unlock()
|
||||||
|
return sw.Solver.Present(ctx, chal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sw solverWrapper) Wait(ctx context.Context, chal acme.Challenge) error {
|
||||||
|
if waiter, ok := sw.Solver.(acmez.Waiter); ok {
|
||||||
|
return waiter.Wait(ctx, chal)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sw solverWrapper) CleanUp(ctx context.Context, chal acme.Challenge) error {
|
||||||
|
activeChallengesMu.Lock()
|
||||||
|
delete(activeChallenges, chal.Identifier.Value)
|
||||||
|
activeChallengesMu.Unlock()
|
||||||
|
return sw.Solver.CleanUp(ctx, chal)
|
||||||
|
}
|
||||||
|
|
||||||
// Interface guards
|
// Interface guards
|
||||||
var (
|
var (
|
||||||
_ acmez.Solver = (*DNS01Solver)(nil)
|
_ acmez.Solver = (*solverWrapper)(nil)
|
||||||
_ acmez.Waiter = (*DNS01Solver)(nil)
|
_ acmez.Waiter = (*solverWrapper)(nil)
|
||||||
|
_ acmez.Waiter = (*distributedSolver)(nil)
|
||||||
)
|
)
|
||||||
|
|
|
@ -16,12 +16,13 @@ package certmagic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log"
|
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage is a type that implements a key-value store.
|
// Storage is a type that implements a key-value store.
|
||||||
|
@ -213,16 +214,20 @@ func (keys KeyBuilder) Safe(str string) string {
|
||||||
// this does not cancel the operations that
|
// this does not cancel the operations that
|
||||||
// the locks are synchronizing, this should be
|
// the locks are synchronizing, this should be
|
||||||
// called only immediately before process exit.
|
// called only immediately before process exit.
|
||||||
func CleanUpOwnLocks() {
|
// Errors are only reported if a logger is given.
|
||||||
|
func CleanUpOwnLocks(logger *zap.Logger) {
|
||||||
locksMu.Lock()
|
locksMu.Lock()
|
||||||
defer locksMu.Unlock()
|
defer locksMu.Unlock()
|
||||||
for lockKey, storage := range locks {
|
for lockKey, storage := range locks {
|
||||||
err := storage.Unlock(lockKey)
|
err := storage.Unlock(lockKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
delete(locks, lockKey)
|
delete(locks, lockKey)
|
||||||
} else {
|
} else if logger != nil {
|
||||||
log.Printf("[ERROR] Unable to clean up lock: %v (lock=%s storage=%s)",
|
logger.Error("unable to clean up lock in storage backend",
|
||||||
err, lockKey, storage)
|
zap.Any("storage", storage),
|
||||||
|
zap.String("lock_key", lockKey),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,6 +277,7 @@ var safeKeyRE = regexp.MustCompile(`[^\w@.-]`)
|
||||||
// ErrNotExist is returned by Storage implementations when
|
// ErrNotExist is returned by Storage implementations when
|
||||||
// a resource is not found. It is similar to os.IsNotExist
|
// a resource is not found. It is similar to os.IsNotExist
|
||||||
// except this is a type, not a variable.
|
// except this is a type, not a variable.
|
||||||
|
// TODO: use new Go error wrapping conventions
|
||||||
type ErrNotExist interface {
|
type ErrNotExist interface {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Makefile for releasing.
|
# Makefile for releasing.
|
||||||
#
|
#
|
||||||
# The release is controlled from version.go. The version found there is
|
# The release is controlled from version.go. The version found there is
|
||||||
# used to tag the git repo, we're not building any artifects so there is nothing
|
# used to tag the git repo, we're not building any artifacts so there is nothing
|
||||||
# to upload to github.
|
# to upload to github.
|
||||||
#
|
#
|
||||||
# * Up the version in version.go
|
# * Up the version in version.go
|
||||||
|
|
|
@ -379,7 +379,7 @@ func Dial(network, address string) (conn *Conn, err error) {
|
||||||
func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
|
func ExchangeContext(ctx context.Context, m *Msg, a string) (r *Msg, err error) {
|
||||||
client := Client{Net: "udp"}
|
client := Client{Net: "udp"}
|
||||||
r, _, err = client.ExchangeContext(ctx, m, a)
|
r, _, err = client.ExchangeContext(ctx, m, a)
|
||||||
// ignorint rtt to leave the original ExchangeContext API unchanged, but
|
// ignoring rtt to leave the original ExchangeContext API unchanged, but
|
||||||
// this function will go away
|
// this function will go away
|
||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -349,10 +349,7 @@ func ReverseAddr(addr string) (arpa string, err error) {
|
||||||
// Add it, in reverse, to the buffer
|
// Add it, in reverse, to the buffer
|
||||||
for i := len(ip) - 1; i >= 0; i-- {
|
for i := len(ip) - 1; i >= 0; i-- {
|
||||||
v := ip[i]
|
v := ip[i]
|
||||||
buf = append(buf, hexDigit[v&0xF])
|
buf = append(buf, hexDigit[v&0xF], '.', hexDigit[v>>4], '.')
|
||||||
buf = append(buf, '.')
|
|
||||||
buf = append(buf, hexDigit[v>>4])
|
|
||||||
buf = append(buf, '.')
|
|
||||||
}
|
}
|
||||||
// Append "ip6.arpa." and return (buf already has the final .)
|
// Append "ip6.arpa." and return (buf already has the final .)
|
||||||
buf = append(buf, "ip6.arpa."...)
|
buf = append(buf, "ip6.arpa."...)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
@ -17,8 +18,6 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DNSSEC encryption algorithm codes.
|
// DNSSEC encryption algorithm codes.
|
||||||
|
@ -500,7 +499,7 @@ func (rr *RRSIG) ValidityPeriod(t time.Time) bool {
|
||||||
return ti <= utc && utc <= te
|
return ti <= utc && utc <= te
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the signatures base64 encodedig sigdata as a byte slice.
|
// Return the signatures base64 encoding sigdata as a byte slice.
|
||||||
func (rr *RRSIG) sigBuf() []byte {
|
func (rr *RRSIG) sigBuf() []byte {
|
||||||
sigbuf, err := fromBase64([]byte(rr.Signature))
|
sigbuf, err := fromBase64([]byte(rr.Signature))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -3,12 +3,11 @@ package dns
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate generates a DNSKEY of the given bit size.
|
// Generate generates a DNSKEY of the given bit size.
|
||||||
|
|
|
@ -4,13 +4,12 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPrivateKey returns a PrivateKey by parsing the string s.
|
// NewPrivateKey returns a PrivateKey by parsing the string s.
|
||||||
|
|
|
@ -3,11 +3,10 @@ package dns
|
||||||
import (
|
import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const format = "Private-key-format: v1.3\n"
|
const format = "Private-key-format: v1.3\n"
|
||||||
|
|
|
@ -525,7 +525,7 @@ func (e *EDNS0_N3U) String() string {
|
||||||
}
|
}
|
||||||
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
func (e *EDNS0_N3U) copy() EDNS0 { return &EDNS0_N3U{e.Code, e.AlgCode} }
|
||||||
|
|
||||||
// EDNS0_EXPIRE implementes the EDNS0 option as described in RFC 7314.
|
// EDNS0_EXPIRE implements the EDNS0 option as described in RFC 7314.
|
||||||
type EDNS0_EXPIRE struct {
|
type EDNS0_EXPIRE struct {
|
||||||
Code uint16 // Always EDNS0EXPIRE
|
Code uint16 // Always EDNS0EXPIRE
|
||||||
Expire uint32
|
Expire uint32
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
module github.com/miekg/dns
|
module github.com/miekg/dns
|
||||||
|
|
||||||
go 1.12
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,39 +1,10 @@
|
||||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4 h1:Vk3wNqEZwyGyei9yq5ekj7frek2u7HUfffJ1/opblzc=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
|
||||||
golang.org/x/crypto v0.0.0-20181001203147-e3636079e1a4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472 h1:Gv7RPwsi3eZ2Fgewe3CBsuOebPwO27PoXzRpJPsvSSM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04 h1:cEhElsAv9LUt9ZUUocxzWe05oFLVd+AA2nstydTeI8g=
|
||||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 h1:dgd4x4kJt7G4k4m93AYLzM8Ni6h2qLTfh9n9vXJT3/0=
|
|
||||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
|
||||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
|
|
||||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611 h1:O33LKL7WyJgjN9CvxfTIomjIClbd/Kq86/iipowHQU0=
|
|
||||||
golang.org/x/sys v0.0.0-20180928133829-e4b3c5e90611/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
|
|
||||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
|
|
||||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
|
||||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ package dns
|
||||||
// escaped dots (\.) for instance.
|
// escaped dots (\.) for instance.
|
||||||
// s must be a syntactically valid domain name, see IsDomainName.
|
// s must be a syntactically valid domain name, see IsDomainName.
|
||||||
func SplitDomainName(s string) (labels []string) {
|
func SplitDomainName(s string) (labels []string) {
|
||||||
if len(s) == 0 {
|
if s == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
fqdnEnd := 0 // offset of the final '.' or the length of the name
|
||||||
|
|
|
@ -742,7 +742,7 @@ func (dns *Msg) packBufferWithCompressionMap(buf []byte, compression compression
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set extended rcode unconditionally if we have an opt, this will allow
|
// Set extended rcode unconditionally if we have an opt, this will allow
|
||||||
// reseting the extended rcode bits if they need to.
|
// resetting the extended rcode bits if they need to.
|
||||||
if opt := dns.IsEdns0(); opt != nil {
|
if opt := dns.IsEdns0(); opt != nil {
|
||||||
opt.SetExtendedRcode(uint16(dns.Rcode))
|
opt.SetExtendedRcode(uint16(dns.Rcode))
|
||||||
} else if dns.Rcode > 0xF {
|
} else if dns.Rcode > 0xF {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import "strings"
|
||||||
// RFC 6895. This allows one to experiment with new RR types, without requesting an
|
// RFC 6895. This allows one to experiment with new RR types, without requesting an
|
||||||
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
|
// official type code. Also see dns.PrivateHandle and dns.PrivateHandleRemove.
|
||||||
type PrivateRdata interface {
|
type PrivateRdata interface {
|
||||||
// String returns the text presentaton of the Rdata of the Private RR.
|
// String returns the text presentation of the Rdata of the Private RR.
|
||||||
String() string
|
String() string
|
||||||
// Parse parses the Rdata of the private RR.
|
// Parse parses the Rdata of the private RR.
|
||||||
Parse([]string) error
|
Parse([]string) error
|
||||||
|
|
|
@ -1233,7 +1233,7 @@ func stringToCm(token string) (e, m uint8, ok bool) {
|
||||||
// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.
|
// 'nn.1' must be treated as 'nn-meters and 10cm, not 1cm.
|
||||||
cmeters *= 10
|
cmeters *= 10
|
||||||
}
|
}
|
||||||
if len(s[0]) == 0 {
|
if s[0] == "" {
|
||||||
// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
|
// This will allow omitting the 'meter' part, like .01 (meaning 0.01m = 1cm).
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -1352,7 +1352,7 @@ func stringToNodeID(l lex) (uint64, *ParseError) {
|
||||||
if len(l.token) < 19 {
|
if len(l.token) < 19 {
|
||||||
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
||||||
}
|
}
|
||||||
// There must be three colons at fixes postitions, if not its a parse error
|
// There must be three colons at fixes positions, if not its a parse error
|
||||||
if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
|
if l.token[4] != ':' && l.token[9] != ':' && l.token[14] != ':' {
|
||||||
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
return 0, &ParseError{l.token, "bad NID/L64 NodeID/Locator64", l}
|
||||||
}
|
}
|
||||||
|
|
|
@ -609,7 +609,7 @@ func (rr *LOC) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 {
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
||||||
return &ParseError{"", "bad LOC Latitude seconds", l}
|
return &ParseError{"", "bad LOC Latitude seconds", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Latitude += uint32(1000 * i)
|
rr.Latitude += uint32(1000 * i)
|
||||||
|
@ -645,7 +645,7 @@ East:
|
||||||
}
|
}
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if i, err := strconv.ParseFloat(l.token, 32); err != nil || l.err || i < 0 || i >= 60 {
|
if i, err := strconv.ParseFloat(l.token, 64); err != nil || l.err || i < 0 || i >= 60 {
|
||||||
return &ParseError{"", "bad LOC Longitude seconds", l}
|
return &ParseError{"", "bad LOC Longitude seconds", l}
|
||||||
} else {
|
} else {
|
||||||
rr.Longitude += uint32(1000 * i)
|
rr.Longitude += uint32(1000 * i)
|
||||||
|
@ -662,7 +662,7 @@ East:
|
||||||
Altitude:
|
Altitude:
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad LOC Altitude", l}
|
return &ParseError{"", "bad LOC Altitude", l}
|
||||||
}
|
}
|
||||||
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
if l.token[len(l.token)-1] == 'M' || l.token[len(l.token)-1] == 'm' {
|
||||||
|
@ -722,7 +722,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad HIP Hit", l}
|
return &ParseError{"", "bad HIP Hit", l}
|
||||||
}
|
}
|
||||||
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
rr.Hit = l.token // This can not contain spaces, see RFC 5205 Section 6.
|
||||||
|
@ -730,7 +730,7 @@ func (rr *HIP) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next() // zBlank
|
c.Next() // zBlank
|
||||||
l, _ = c.Next() // zString
|
l, _ = c.Next() // zString
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad HIP PublicKey", l}
|
return &ParseError{"", "bad HIP PublicKey", l}
|
||||||
}
|
}
|
||||||
rr.PublicKey = l.token // This cannot contain spaces
|
rr.PublicKey = l.token // This cannot contain spaces
|
||||||
|
@ -846,6 +846,38 @@ func (rr *CSYNC) parse(c *zlexer, o string) *ParseError {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rr *ZONEMD) parse(c *zlexer, o string) *ParseError {
|
||||||
|
l, _ := c.Next()
|
||||||
|
i, e := strconv.ParseUint(l.token, 10, 32)
|
||||||
|
if e != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Serial", l}
|
||||||
|
}
|
||||||
|
rr.Serial = uint32(i)
|
||||||
|
|
||||||
|
c.Next() // zBlank
|
||||||
|
l, _ = c.Next()
|
||||||
|
i, e1 := strconv.ParseUint(l.token, 10, 8)
|
||||||
|
if e1 != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Scheme", l}
|
||||||
|
}
|
||||||
|
rr.Scheme = uint8(i)
|
||||||
|
|
||||||
|
c.Next() // zBlank
|
||||||
|
l, _ = c.Next()
|
||||||
|
i, err := strconv.ParseUint(l.token, 10, 8)
|
||||||
|
if err != nil || l.err {
|
||||||
|
return &ParseError{"", "bad ZONEMD Hash Algorithm", l}
|
||||||
|
}
|
||||||
|
rr.Hash = uint8(i)
|
||||||
|
|
||||||
|
s, e2 := endingToString(c, "bad ZONEMD Digest")
|
||||||
|
if e2 != nil {
|
||||||
|
return e2
|
||||||
|
}
|
||||||
|
rr.Digest = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) }
|
func (rr *SIG) parse(c *zlexer, o string) *ParseError { return rr.RRSIG.parse(c, o) }
|
||||||
|
|
||||||
func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
func (rr *RRSIG) parse(c *zlexer, o string) *ParseError {
|
||||||
|
@ -997,7 +1029,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError {
|
||||||
rr.Iterations = uint16(i)
|
rr.Iterations = uint16(i)
|
||||||
c.Next()
|
c.Next()
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 Salt", l}
|
return &ParseError{"", "bad NSEC3 Salt", l}
|
||||||
}
|
}
|
||||||
if l.token != "-" {
|
if l.token != "-" {
|
||||||
|
@ -1007,7 +1039,7 @@ func (rr *NSEC3) parse(c *zlexer, o string) *ParseError {
|
||||||
|
|
||||||
c.Next()
|
c.Next()
|
||||||
l, _ = c.Next()
|
l, _ = c.Next()
|
||||||
if len(l.token) == 0 || l.err {
|
if l.token == "" || l.err {
|
||||||
return &ParseError{"", "bad NSEC3 NextDomain", l}
|
return &ParseError{"", "bad NSEC3 NextDomain", l}
|
||||||
}
|
}
|
||||||
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
rr.HashLength = 20 // Fix for NSEC3 (sha1 160 bits)
|
||||||
|
|
|
@ -17,7 +17,7 @@ func (rr *SIG) Sign(k crypto.Signer, m *Msg) ([]byte, error) {
|
||||||
if k == nil {
|
if k == nil {
|
||||||
return nil, ErrPrivKey
|
return nil, ErrPrivKey
|
||||||
}
|
}
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
|
||||||
return nil, ErrKey
|
return nil, ErrKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ func (rr *SIG) Verify(k *KEY, buf []byte) error {
|
||||||
if k == nil {
|
if k == nil {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
if rr.KeyTag == 0 || len(rr.SignerName) == 0 || rr.Algorithm == 0 {
|
if rr.KeyTag == 0 || rr.SignerName == "" || rr.Algorithm == 0 {
|
||||||
return ErrKey
|
return ErrKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -321,7 +321,7 @@ func (s *SVCBAlpn) pack() ([]byte, error) {
|
||||||
// Liberally estimate the size of an alpn as 10 octets
|
// Liberally estimate the size of an alpn as 10 octets
|
||||||
b := make([]byte, 0, 10*len(s.Alpn))
|
b := make([]byte, 0, 10*len(s.Alpn))
|
||||||
for _, e := range s.Alpn {
|
for _, e := range s.Alpn {
|
||||||
if len(e) == 0 {
|
if e == "" {
|
||||||
return nil, errors.New("dns: svcbalpn: empty alpn-id")
|
return nil, errors.New("dns: svcbalpn: empty alpn-id")
|
||||||
}
|
}
|
||||||
if len(e) > 255 {
|
if len(e) > 255 {
|
||||||
|
@ -390,7 +390,7 @@ func (*SVCBNoDefaultAlpn) unpack(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*SVCBNoDefaultAlpn) parse(b string) error {
|
func (*SVCBNoDefaultAlpn) parse(b string) error {
|
||||||
if len(b) != 0 {
|
if b != "" {
|
||||||
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
|
return errors.New("dns: svcbnodefaultalpn: no_default_alpn must have no value")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -81,6 +81,7 @@ const (
|
||||||
TypeCDNSKEY uint16 = 60
|
TypeCDNSKEY uint16 = 60
|
||||||
TypeOPENPGPKEY uint16 = 61
|
TypeOPENPGPKEY uint16 = 61
|
||||||
TypeCSYNC uint16 = 62
|
TypeCSYNC uint16 = 62
|
||||||
|
TypeZONEMD uint16 = 63
|
||||||
TypeSVCB uint16 = 64
|
TypeSVCB uint16 = 64
|
||||||
TypeHTTPS uint16 = 65
|
TypeHTTPS uint16 = 65
|
||||||
TypeSPF uint16 = 99
|
TypeSPF uint16 = 99
|
||||||
|
@ -150,6 +151,17 @@ const (
|
||||||
OpcodeUpdate = 5
|
OpcodeUpdate = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Used in ZONEMD https://tools.ietf.org/html/rfc8976
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ZoneMD Accepted Schemes
|
||||||
|
ZoneMDSchemeSimple = 1
|
||||||
|
|
||||||
|
// ZoneMD Hash Algorithms
|
||||||
|
ZoneMDHashAlgSHA384 = 1
|
||||||
|
ZoneMDHashAlgSHA512 = 2
|
||||||
|
)
|
||||||
|
|
||||||
// Header is the wire format for the DNS packet header.
|
// Header is the wire format for the DNS packet header.
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Id uint16
|
Id uint16
|
||||||
|
@ -1361,6 +1373,23 @@ func (rr *CSYNC) len(off int, compression map[string]struct{}) int {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ZONEMD RR, from draft-ietf-dnsop-dns-zone-digest
|
||||||
|
type ZONEMD struct {
|
||||||
|
Hdr RR_Header
|
||||||
|
Serial uint32
|
||||||
|
Scheme uint8
|
||||||
|
Hash uint8
|
||||||
|
Digest string `dns:"hex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rr *ZONEMD) String() string {
|
||||||
|
return rr.Hdr.String() +
|
||||||
|
strconv.Itoa(int(rr.Serial)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Scheme)) +
|
||||||
|
" " + strconv.Itoa(int(rr.Hash)) +
|
||||||
|
" " + rr.Digest
|
||||||
|
}
|
||||||
|
|
||||||
// APL RR. See RFC 3123.
|
// APL RR. See RFC 3123.
|
||||||
type APL struct {
|
type APL struct {
|
||||||
Hdr RR_Header
|
Hdr RR_Header
|
||||||
|
@ -1472,7 +1501,7 @@ func StringToTime(s string) (uint32, error) {
|
||||||
|
|
||||||
// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
|
// saltToString converts a NSECX salt to uppercase and returns "-" when it is empty.
|
||||||
func saltToString(s string) string {
|
func saltToString(s string) string {
|
||||||
if len(s) == 0 {
|
if s == "" {
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
return strings.ToUpper(s)
|
return strings.ToUpper(s)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package dns
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Version is current version of this library.
|
// Version is current version of this library.
|
||||||
var Version = v{1, 1, 40}
|
var Version = v{1, 1, 41}
|
||||||
|
|
||||||
// v holds the version of this library.
|
// v holds the version of this library.
|
||||||
type v struct {
|
type v struct {
|
||||||
|
|
|
@ -1317,3 +1317,24 @@ func (r1 *X25) isDuplicate(_r2 RR) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r1 *ZONEMD) isDuplicate(_r2 RR) bool {
|
||||||
|
r2, ok := _r2.(*ZONEMD)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
_ = r2
|
||||||
|
if r1.Serial != r2.Serial {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r1.Scheme != r2.Scheme {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r1.Hash != r2.Hash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if r1.Digest != r2.Digest {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -1118,6 +1118,26 @@ func (rr *X25) pack(msg []byte, off int, compression compressionMap, compress bo
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rr *ZONEMD) pack(msg []byte, off int, compression compressionMap, compress bool) (off1 int, err error) {
|
||||||
|
off, err = packUint32(rr.Serial, msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
off, err = packUint8(rr.Scheme, msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
off, err = packUint8(rr.Hash, msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
off, err = packStringHex(rr.Digest, msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
|
||||||
// unpack*() functions
|
// unpack*() functions
|
||||||
|
|
||||||
func (rr *A) unpack(msg []byte, off int) (off1 int, err error) {
|
func (rr *A) unpack(msg []byte, off int) (off1 int, err error) {
|
||||||
|
@ -2821,3 +2841,35 @@ func (rr *X25) unpack(msg []byte, off int) (off1 int, err error) {
|
||||||
}
|
}
|
||||||
return off, nil
|
return off, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rr *ZONEMD) unpack(msg []byte, off int) (off1 int, err error) {
|
||||||
|
rdStart := off
|
||||||
|
_ = rdStart
|
||||||
|
|
||||||
|
rr.Serial, off, err = unpackUint32(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
if off == len(msg) {
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
rr.Scheme, off, err = unpackUint8(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
if off == len(msg) {
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
rr.Hash, off, err = unpackUint8(msg, off)
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
if off == len(msg) {
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
rr.Digest, off, err = unpackStringHex(msg, off, rdStart+int(rr.Hdr.Rdlength))
|
||||||
|
if err != nil {
|
||||||
|
return off, err
|
||||||
|
}
|
||||||
|
return off, nil
|
||||||
|
}
|
||||||
|
|
|
@ -82,6 +82,7 @@ var TypeToRR = map[uint16]func() RR{
|
||||||
TypeUINFO: func() RR { return new(UINFO) },
|
TypeUINFO: func() RR { return new(UINFO) },
|
||||||
TypeURI: func() RR { return new(URI) },
|
TypeURI: func() RR { return new(URI) },
|
||||||
TypeX25: func() RR { return new(X25) },
|
TypeX25: func() RR { return new(X25) },
|
||||||
|
TypeZONEMD: func() RR { return new(ZONEMD) },
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeToString is a map of strings for each RR type.
|
// TypeToString is a map of strings for each RR type.
|
||||||
|
@ -168,6 +169,7 @@ var TypeToString = map[uint16]string{
|
||||||
TypeUNSPEC: "UNSPEC",
|
TypeUNSPEC: "UNSPEC",
|
||||||
TypeURI: "URI",
|
TypeURI: "URI",
|
||||||
TypeX25: "X25",
|
TypeX25: "X25",
|
||||||
|
TypeZONEMD: "ZONEMD",
|
||||||
TypeNSAPPTR: "NSAP-PTR",
|
TypeNSAPPTR: "NSAP-PTR",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +247,7 @@ func (rr *UID) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
|
func (rr *UINFO) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
func (rr *URI) Header() *RR_Header { return &rr.Hdr }
|
||||||
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
func (rr *X25) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
func (rr *ZONEMD) Header() *RR_Header { return &rr.Hdr }
|
||||||
|
|
||||||
// len() functions
|
// len() functions
|
||||||
func (rr *A) len(off int, compression map[string]struct{}) int {
|
func (rr *A) len(off int, compression map[string]struct{}) int {
|
||||||
|
@ -684,6 +687,14 @@ func (rr *X25) len(off int, compression map[string]struct{}) int {
|
||||||
l += len(rr.PSDNAddress) + 1
|
l += len(rr.PSDNAddress) + 1
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
func (rr *ZONEMD) len(off int, compression map[string]struct{}) int {
|
||||||
|
l := rr.Hdr.len(off, compression)
|
||||||
|
l += 4 // Serial
|
||||||
|
l++ // Scheme
|
||||||
|
l++ // Hash
|
||||||
|
l += len(rr.Digest) / 2
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
// copy() functions
|
// copy() functions
|
||||||
func (rr *A) copy() RR {
|
func (rr *A) copy() RR {
|
||||||
|
@ -936,3 +947,6 @@ func (rr *URI) copy() RR {
|
||||||
func (rr *X25) copy() RR {
|
func (rr *X25) copy() RR {
|
||||||
return &X25{rr.Hdr, rr.PSDNAddress}
|
return &X25{rr.Hdr, rr.PSDNAddress}
|
||||||
}
|
}
|
||||||
|
func (rr *ZONEMD) copy() RR {
|
||||||
|
return &ZONEMD{rr.Hdr, rr.Serial, rr.Scheme, rr.Hash, rr.Digest}
|
||||||
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ multiply:
|
||||||
BGE loop
|
BGE loop
|
||||||
|
|
||||||
bytes_between_0_and_15:
|
bytes_between_0_and_15:
|
||||||
CMP $0, R5
|
CMP R5, $0
|
||||||
BEQ done
|
BEQ done
|
||||||
MOVD $0, R16 // h0
|
MOVD $0, R16 // h0
|
||||||
MOVD $0, R17 // h1
|
MOVD $0, R17 // h1
|
||||||
|
@ -122,7 +122,7 @@ just1:
|
||||||
// Exactly 8
|
// Exactly 8
|
||||||
MOVD (R4), R16
|
MOVD (R4), R16
|
||||||
|
|
||||||
CMP $0, R17
|
CMP R17, $0
|
||||||
|
|
||||||
// Check if we've already set R17; if not
|
// Check if we've already set R17; if not
|
||||||
// set 1 to indicate end of msg.
|
// set 1 to indicate end of msg.
|
||||||
|
@ -151,7 +151,7 @@ less4:
|
||||||
ADD $2, R4
|
ADD $2, R4
|
||||||
|
|
||||||
less2:
|
less2:
|
||||||
CMP $0, R5
|
CMP R5, $0
|
||||||
BEQ insert1
|
BEQ insert1
|
||||||
MOVBZ (R4), R21
|
MOVBZ (R4), R21
|
||||||
SLD R22, R21, R21
|
SLD R22, R21, R21
|
||||||
|
@ -166,12 +166,12 @@ insert1:
|
||||||
|
|
||||||
carry:
|
carry:
|
||||||
// Add new values to h0, h1, h2
|
// Add new values to h0, h1, h2
|
||||||
ADDC R16, R8
|
ADDC R16, R8
|
||||||
ADDE R17, R9
|
ADDE R17, R9
|
||||||
ADDE $0, R10
|
ADDZE R10, R10
|
||||||
MOVD $16, R5
|
MOVD $16, R5
|
||||||
ADD R5, R4
|
ADD R5, R4
|
||||||
BR multiply
|
BR multiply
|
||||||
|
|
||||||
done:
|
done:
|
||||||
// Save h0, h1, h2 in state
|
// Save h0, h1, h2 in state
|
||||||
|
|
|
@ -9,6 +9,7 @@ package scrypt // import "golang.org/x/crypto/scrypt"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
|
||||||
|
@ -143,36 +144,34 @@ func integer(b []uint32, r int) uint64 {
|
||||||
|
|
||||||
func smix(b []byte, r, N int, v, xy []uint32) {
|
func smix(b []byte, r, N int, v, xy []uint32) {
|
||||||
var tmp [16]uint32
|
var tmp [16]uint32
|
||||||
|
R := 32 * r
|
||||||
x := xy
|
x := xy
|
||||||
y := xy[32*r:]
|
y := xy[R:]
|
||||||
|
|
||||||
j := 0
|
j := 0
|
||||||
for i := 0; i < 32*r; i++ {
|
for i := 0; i < R; i++ {
|
||||||
x[i] = uint32(b[j]) | uint32(b[j+1])<<8 | uint32(b[j+2])<<16 | uint32(b[j+3])<<24
|
x[i] = binary.LittleEndian.Uint32(b[j:])
|
||||||
j += 4
|
j += 4
|
||||||
}
|
}
|
||||||
for i := 0; i < N; i += 2 {
|
for i := 0; i < N; i += 2 {
|
||||||
blockCopy(v[i*(32*r):], x, 32*r)
|
blockCopy(v[i*R:], x, R)
|
||||||
blockMix(&tmp, x, y, r)
|
blockMix(&tmp, x, y, r)
|
||||||
|
|
||||||
blockCopy(v[(i+1)*(32*r):], y, 32*r)
|
blockCopy(v[(i+1)*R:], y, R)
|
||||||
blockMix(&tmp, y, x, r)
|
blockMix(&tmp, y, x, r)
|
||||||
}
|
}
|
||||||
for i := 0; i < N; i += 2 {
|
for i := 0; i < N; i += 2 {
|
||||||
j := int(integer(x, r) & uint64(N-1))
|
j := int(integer(x, r) & uint64(N-1))
|
||||||
blockXOR(x, v[j*(32*r):], 32*r)
|
blockXOR(x, v[j*R:], R)
|
||||||
blockMix(&tmp, x, y, r)
|
blockMix(&tmp, x, y, r)
|
||||||
|
|
||||||
j = int(integer(y, r) & uint64(N-1))
|
j = int(integer(y, r) & uint64(N-1))
|
||||||
blockXOR(y, v[j*(32*r):], 32*r)
|
blockXOR(y, v[j*R:], R)
|
||||||
blockMix(&tmp, y, x, r)
|
blockMix(&tmp, y, x, r)
|
||||||
}
|
}
|
||||||
j = 0
|
j = 0
|
||||||
for _, v := range x[:32*r] {
|
for _, v := range x[:R] {
|
||||||
b[j+0] = byte(v >> 0)
|
binary.LittleEndian.PutUint32(b[j:], v)
|
||||||
b[j+1] = byte(v >> 8)
|
|
||||||
b[j+2] = byte(v >> 16)
|
|
||||||
b[j+3] = byte(v >> 24)
|
|
||||||
j += 4
|
j += 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
|
||||||
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
|
||||||
package socket
|
package socket
|
||||||
|
|
||||||
|
@ -15,4 +15,7 @@ const (
|
||||||
sysAF_INET6 = unix.AF_INET6
|
sysAF_INET6 = unix.AF_INET6
|
||||||
|
|
||||||
sysSOCK_RAW = unix.SOCK_RAW
|
sysSOCK_RAW = unix.SOCK_RAW
|
||||||
|
|
||||||
|
sizeofSockaddrInet4 = unix.SizeofSockaddrInet4
|
||||||
|
sizeofSockaddrInet6 = unix.SizeofSockaddrInet6
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2020 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build zos
|
|
||||||
// +build zos
|
|
||||||
|
|
||||||
package socket
|
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
const (
|
|
||||||
sysAF_UNSPEC = syscall.AF_UNSPEC
|
|
||||||
sysAF_INET = syscall.AF_INET
|
|
||||||
sysAF_INET6 = syscall.AF_INET6
|
|
||||||
|
|
||||||
sysSOCK_RAW = syscall.SOCK_RAW
|
|
||||||
)
|
|
|
@ -32,12 +32,12 @@ func marshalInetAddr(a net.Addr) []byte {
|
||||||
|
|
||||||
func marshalSockaddr(ip net.IP, port int, zone string) []byte {
|
func marshalSockaddr(ip net.IP, port int, zone string) []byte {
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
b := make([]byte, sizeofSockaddrInet)
|
b := make([]byte, sizeofSockaddrInet4)
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "android", "illumos", "linux", "solaris", "windows":
|
case "android", "illumos", "linux", "solaris", "windows":
|
||||||
NativeEndian.PutUint16(b[:2], uint16(sysAF_INET))
|
NativeEndian.PutUint16(b[:2], uint16(sysAF_INET))
|
||||||
default:
|
default:
|
||||||
b[0] = sizeofSockaddrInet
|
b[0] = sizeofSockaddrInet4
|
||||||
b[1] = sysAF_INET
|
b[1] = sysAF_INET
|
||||||
}
|
}
|
||||||
binary.BigEndian.PutUint16(b[2:4], uint16(port))
|
binary.BigEndian.PutUint16(b[2:4], uint16(port))
|
||||||
|
@ -77,7 +77,7 @@ func parseInetAddr(b []byte, network string) (net.Addr, error) {
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
var zone string
|
var zone string
|
||||||
if af == sysAF_INET {
|
if af == sysAF_INET {
|
||||||
if len(b) < sizeofSockaddrInet {
|
if len(b) < sizeofSockaddrInet4 {
|
||||||
return nil, errors.New("short address")
|
return nil, errors.New("short address")
|
||||||
}
|
}
|
||||||
ip = make(net.IP, net.IPv4len)
|
ip = make(net.IP, net.IPv4len)
|
||||||
|
|
|
@ -15,6 +15,9 @@ const (
|
||||||
sysAF_INET6 = 0xa
|
sysAF_INET6 = 0xa
|
||||||
|
|
||||||
sysSOCK_RAW = 0x3
|
sysSOCK_RAW = 0x3
|
||||||
|
|
||||||
|
sizeofSockaddrInet4 = 0x10
|
||||||
|
sizeofSockaddrInet6 = 0x1c
|
||||||
)
|
)
|
||||||
|
|
||||||
func marshalInetAddr(ip net.IP, port int, zone string) []byte {
|
func marshalInetAddr(ip net.IP, port int, zone string) []byte {
|
||||||
|
|
|
@ -22,25 +22,8 @@ const (
|
||||||
sysAF_INET6 = windows.AF_INET6
|
sysAF_INET6 = windows.AF_INET6
|
||||||
|
|
||||||
sysSOCK_RAW = windows.SOCK_RAW
|
sysSOCK_RAW = windows.SOCK_RAW
|
||||||
)
|
|
||||||
|
|
||||||
type sockaddrInet struct {
|
sizeofSockaddrInet4 = 0x10
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
sizeofSockaddrInet6 = 0x1c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -34,27 +34,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,25 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,25 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,25 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,25 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -34,25 +34,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,25 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
X__pad [8]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x38
|
sizeofMsghdr = 0x38
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,27 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,27 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -29,27 +29,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,27 +32,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x8
|
sizeofIovec = 0x8
|
||||||
sizeofMsghdr = 0x1c
|
sizeofMsghdr = 0x1c
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,27 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -24,27 +24,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Len uint8
|
|
||||||
Family uint8
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x1c
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,26 +26,7 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sockaddrInet struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Addr [4]byte /* in_addr */
|
|
||||||
Zero [8]int8
|
|
||||||
}
|
|
||||||
|
|
||||||
type sockaddrInet6 struct {
|
|
||||||
Family uint16
|
|
||||||
Port uint16
|
|
||||||
Flowinfo uint32
|
|
||||||
Addr [16]byte /* in6_addr */
|
|
||||||
Scope_id uint32
|
|
||||||
X__sin6_src_id uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sizeofIovec = 0x10
|
sizeofIovec = 0x10
|
||||||
sizeofMsghdr = 0x30
|
sizeofMsghdr = 0x30
|
||||||
|
|
||||||
sizeofSockaddrInet = 0x10
|
|
||||||
sizeofSockaddrInet6 = 0x20
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,8 +25,4 @@ type cmsghdr struct {
|
||||||
Type int32
|
Type int32
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const sizeofCmsghdr = 12
|
||||||
sizeofCmsghdr = 12
|
|
||||||
sizeofSockaddrInet = 16
|
|
||||||
sizeofSockaddrInet6 = 28
|
|
||||||
)
|
|
||||||
|
|
|
@ -14,11 +14,13 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/internal/iana"
|
"golang.org/x/net/internal/iana"
|
||||||
"golang.org/x/net/internal/socket"
|
"golang.org/x/net/internal/socket"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func marshalDst(b []byte, cm *ControlMessage) []byte {
|
func marshalDst(b []byte, cm *ControlMessage) []byte {
|
||||||
m := socket.ControlMessage(b)
|
m := socket.ControlMessage(b)
|
||||||
m.MarshalHeader(iana.ProtocolIP, sysIP_RECVDSTADDR, net.IPv4len)
|
m.MarshalHeader(iana.ProtocolIP, unix.IP_RECVDSTADDR, net.IPv4len)
|
||||||
return m.Next(net.IPv4len)
|
return m.Next(net.IPv4len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +33,7 @@ func parseDst(cm *ControlMessage, b []byte) {
|
||||||
|
|
||||||
func marshalInterface(b []byte, cm *ControlMessage) []byte {
|
func marshalInterface(b []byte, cm *ControlMessage) []byte {
|
||||||
m := socket.ControlMessage(b)
|
m := socket.ControlMessage(b)
|
||||||
m.MarshalHeader(iana.ProtocolIP, sysIP_RECVIF, syscall.SizeofSockaddrDatalink)
|
m.MarshalHeader(iana.ProtocolIP, sockoptReceiveInterface, syscall.SizeofSockaddrDatalink)
|
||||||
return m.Next(syscall.SizeofSockaddrDatalink)
|
return m.Next(syscall.SizeofSockaddrDatalink)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/internal/iana"
|
"golang.org/x/net/internal/iana"
|
||||||
"golang.org/x/net/internal/socket"
|
"golang.org/x/net/internal/socket"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
|
func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
|
||||||
|
@ -65,7 +67,7 @@ func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) er
|
||||||
|
|
||||||
func marshalTTL(b []byte, cm *ControlMessage) []byte {
|
func marshalTTL(b []byte, cm *ControlMessage) []byte {
|
||||||
m := socket.ControlMessage(b)
|
m := socket.ControlMessage(b)
|
||||||
m.MarshalHeader(iana.ProtocolIP, sysIP_RECVTTL, 1)
|
m.MarshalHeader(iana.ProtocolIP, unix.IP_RECVTTL, 1)
|
||||||
return m.Next(1)
|
return m.Next(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IP_RECVIF is defined on AIX but doesn't work. IP_RECVINTERFACE must be used instead.
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVINTERFACE
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -17,6 +17,8 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVIF
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVIF
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -14,6 +14,8 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVIF
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -17,6 +17,8 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVIF
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 1, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -15,6 +15,8 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sockoptReceiveInterface = unix.IP_RECVIF
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctlOpts = [ctlMax]ctlOpt{
|
ctlOpts = [ctlMax]ctlOpt{
|
||||||
ctlTTL: {unix.IP_RECVTTL, 4, marshalTTL, parseTTL},
|
ctlTTL: {unix.IP_RECVTTL, 4, marshalTTL, parseTTL},
|
||||||
|
|
|
@ -8,10 +8,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x20
|
|
||||||
sysIP_RECVTTL = 0x22
|
|
||||||
|
|
||||||
sizeofIPMreq = 0x8
|
sizeofIPMreq = 0x8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x18
|
|
||||||
|
|
||||||
sizeofSockaddrStorage = 0x80
|
sizeofSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
sizeofInetPktinfo = 0xc
|
sizeofInetPktinfo = 0xc
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x41
|
|
||||||
|
|
||||||
sizeofIPMreq = 0x8
|
sizeofIPMreq = 0x8
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x41
|
|
||||||
|
|
||||||
sizeofSockaddrStorage = 0x80
|
sizeofSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x41
|
|
||||||
|
|
||||||
sizeofSockaddrStorage = 0x80
|
sizeofSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x41
|
|
||||||
|
|
||||||
sizeofSockaddrStorage = 0x80
|
sizeofSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVDSTADDR = 0x7
|
|
||||||
sysIP_RECVIF = 0x14
|
|
||||||
sysIP_RECVTTL = 0x41
|
|
||||||
|
|
||||||
sizeofSockaddrStorage = 0x80
|
sizeofSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVTTL = 0xc
|
|
||||||
|
|
||||||
sizeofKernelSockaddrStorage = 0x80
|
sizeofKernelSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
sizeofInetPktinfo = 0xc
|
sizeofInetPktinfo = 0xc
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVTTL = 0xc
|
|
||||||
|
|
||||||
sizeofKernelSockaddrStorage = 0x80
|
sizeofKernelSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
sizeofInetPktinfo = 0xc
|
sizeofInetPktinfo = 0xc
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
package ipv4
|
package ipv4
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sysIP_RECVTTL = 0xc
|
|
||||||
|
|
||||||
sizeofKernelSockaddrStorage = 0x80
|
sizeofKernelSockaddrStorage = 0x80
|
||||||
sizeofSockaddrInet = 0x10
|
sizeofSockaddrInet = 0x10
|
||||||
sizeofInetPktinfo = 0xc
|
sizeofInetPktinfo = 0xc
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue