Unverified Commit f8aa94f0 authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #76466 from dims/remove-unnecessary-cmd-cfssl

remove unnecessary vendoring of cmd/cfssl
parents 90a939ec 437a7333
......@@ -24,8 +24,6 @@ import (
_ "github.com/bazelbuild/buildtools/buildozer"
_ "github.com/cespare/prettybench"
_ "github.com/client9/misspell/cmd/misspell"
_ "github.com/cloudflare/cfssl/cmd/cfssl"
_ "github.com/cloudflare/cfssl/cmd/cfssljson"
_ "github.com/jstemmer/go-junit-report"
_ "github.com/jteeuwen/go-bindata/go-bindata"
_ "github.com/onsi/ginkgo/ginkgo"
......
......@@ -11,7 +11,6 @@ require (
cloud.google.com/go v0.0.0-20160913182117-3b1ae45394a2
github.com/Azure/azure-sdk-for-go v21.3.0+incompatible
github.com/Azure/go-autorest v11.1.0+incompatible
github.com/GeertJohan/go.rice v0.0.0-20170420135705-c02ca9a983da // indirect
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20181220005116-f8e995905100
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd
......@@ -47,7 +46,6 @@ require (
github.com/cyphar/filepath-securejoin v0.0.0-20170720062807-ae69057f2299 // indirect
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
github.com/d2g/dhcp4client v0.0.0-20170829104524-6e570ed0a266
github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb // indirect
github.com/davecgh/go-spew v1.1.1
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd
github.com/dnaeon/go-vcr v1.0.1 // indirect
......@@ -68,7 +66,6 @@ require (
github.com/go-openapi/strfmt v0.17.0
github.com/go-openapi/validate v0.18.0
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible // indirect
github.com/go-sql-driver/mysql v1.3.0 // indirect
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55
github.com/gogo/protobuf v1.1.1
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903
......@@ -90,19 +87,15 @@ require (
github.com/heketi/rest v0.0.0-20180404230133-aa6a65207413 // indirect
github.com/heketi/tests v0.0.0-20151005000721-f3775cbcefd6 // indirect
github.com/heketi/utils v0.0.0-20170317161834-435bc5bdfa64 // indirect
github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 // indirect
github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade // indirect
github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024
github.com/jteeuwen/go-bindata v0.0.0-20151023091102-a0ff2567cfb7
github.com/kardianos/osext v0.0.0-20150410034420-8fef92e41e22
github.com/karrick/godirwalk v1.7.5 // indirect
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49 // indirect
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 // indirect
github.com/kr/pretty v0.0.0-20140812000539-f31442d60e51 // indirect
github.com/kr/text v0.0.0-20130911015532-6807e777504f // indirect
github.com/lib/pq v0.0.0-20180201184707-88edab080323 // indirect
github.com/libopenstorage/openstorage v0.0.0-20170906232338-093a0c388875
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de
github.com/lithammer/dedent v1.1.0
......@@ -110,7 +103,6 @@ require (
github.com/magiconair/properties v0.0.0-20160816085511-61b492c03cf4 // indirect
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c // indirect
github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de // indirect
github.com/mattn/go-sqlite3 v1.6.0 // indirect
github.com/mesos/mesos-go v0.0.9 // indirect
github.com/mholt/caddy v0.0.0-20180213163048-2de495001514
github.com/miekg/dns v0.0.0-20160614162101-5d001d020961
......
......@@ -11,8 +11,6 @@ github.com/Azure/go-autorest v11.1.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSW
github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/GeertJohan/go.rice v0.0.0-20170420135705-c02ca9a983da h1:UVU3a9pRUyLdnBtn60WjRl0s4SEyJc2ChCY56OAR6wI=
github.com/GeertJohan/go.rice v0.0.0-20170420135705-c02ca9a983da/go.mod h1:DgrzXonpdQbfN3uYaGz1EG4Sbhyum/MMIn6Cphlh2bw=
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20181220005116-f8e995905100 h1:EXrpgbxknFeRM6X/OgRqcbVtl5zqUFD5eV8O8M1BLzo=
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20181220005116-f8e995905100/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14=
github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab h1:UKkYhof1njT1/xq4SEg5z+VpTgjmNeHwPGRQl7takDI=
......@@ -97,8 +95,6 @@ github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQw
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v0.0.0-20170829104524-6e570ed0a266 h1:1iKpuOrCnaRBKKNoChzmVDXeoFuCS7wsooRncvzVPxo=
github.com/d2g/dhcp4client v0.0.0-20170829104524-6e570ed0a266/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb h1:tUf55Po0vzOendQ7NWytcdK0VuzQmfAgvGBUOQvN0WA=
github.com/daaku/go.zipexe v0.0.0-20150329023125-a5fe2436ffcb/go.mod h1:U0vRfAucUOohvdCxt5MWLF+TePIL0xbCkbKIiV8TQCE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd h1:uVsMphB1eRx7xB1njzL3fuMdWRN8HtVzoUOItHMwv5c=
......@@ -161,8 +157,6 @@ github.com/go-openapi/validate v0.18.0 h1:PVXYcP1GkTl+XIAJnyJxOmK6CSG5Q1UcvoCvNO
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM=
github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE=
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55 h1:oIgNYSrSUbNH5DJh6DMhU1PiOKOYIHNxrV3djLsLpEI=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
......@@ -233,10 +227,6 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4=
github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo=
github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade h1:ryslCsfLTV4Cm/9NXqCJirlbYodWqFiTH454IaSn/fY=
github.com/jmoiron/sqlx v0.0.0-20180124204410-05cef0741ade/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c h1:XpRROA6ssPlTwJI8/pH+61uieOkcJhmAFz25cu0B94Y=
github.com/jonboulle/clockwork v0.0.0-20141017032234-72f9bd7c4e0c/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be h1:AHimNtVIpiBjPUhEF5KNCkrUyqTSA5zWUl8sQ2bfGBE=
......@@ -252,8 +242,6 @@ github.com/kardianos/osext v0.0.0-20150410034420-8fef92e41e22 h1:eLCQd4nxsC7sumk
github.com/kardianos/osext v0.0.0-20150410034420-8fef92e41e22/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/karrick/godirwalk v1.7.5 h1:VbzFqwXwNbAZoA6W5odrLr+hKK197CcENcPh6E/gJ0M=
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49 h1:o/c0aWEP/m6n61xlYW2QP4t9424qlJOsxugn5Zds2Rg=
github.com/kisielk/sqlstruct v0.0.0-20150923205031-648daed35d49/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.0.0-20131111012553-2788f0dbd169 h1:YUrU1/jxRqnt0PSrKj1Uj/wEjk/fjnE80QFfi2Zlj7Q=
......@@ -263,8 +251,6 @@ github.com/kr/pretty v0.0.0-20140812000539-f31442d60e51 h1:kGEU5h0EzkNa+B8Q3e0Gl
github.com/kr/pretty v0.0.0-20140812000539-f31442d60e51/go.mod h1:Bvhd+E3laJ0AVkG0c9rmtZcnhV0HQ3+c3YxxqTvc/gA=
github.com/kr/text v0.0.0-20130911015532-6807e777504f h1:JaNmHIV9Eby6srQVWuiQ6n8ko2o/lG6udSRCbFZe1fs=
github.com/kr/text v0.0.0-20130911015532-6807e777504f/go.mod h1:sjUstKUATFIcff4qlB53Kml0wQPtJVc/3fWrmuUmcfA=
github.com/lib/pq v0.0.0-20180201184707-88edab080323 h1:Ou506ViB5uo2GloKFWIYi5hwRJn4AAOXuLVv8RMY9+4=
github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/libopenstorage/openstorage v0.0.0-20170906232338-093a0c388875 h1:s+gZ8/rkYrWgN7xTMUISDw6nspgWyju2OCKmpFBrviQ=
github.com/libopenstorage/openstorage v0.0.0-20170906232338-093a0c388875/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
......@@ -281,8 +267,6 @@ github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c h1:N7uWGS2fTwH/4BwxbHi
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de h1:ryDLMbZGgf2bSdLfdQFaJuuP4No40cDUt62Mdv+3TW8=
github.com/mattn/go-shellwords v0.0.0-20180605041737-f8471b0a71de/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-sqlite3 v1.6.0 h1:TDwTWbeII+88Qy55nWlof0DclgAtI4LqGujkYMzmQII=
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mesos/mesos-go v0.0.9 h1:w8V5sOEnxzHZ2kAOy273v/HgbolyI6XI+qe5jx5u+Y0=
......
......@@ -22,7 +22,6 @@ filegroup(
"//vendor/github.com/Azure/go-autorest/autorest:all-srcs",
"//vendor/github.com/Azure/go-autorest/logger:all-srcs",
"//vendor/github.com/Azure/go-autorest/version:all-srcs",
"//vendor/github.com/GeertJohan/go.rice:all-srcs",
"//vendor/github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud:all-srcs",
"//vendor/github.com/JeffAshton/win_pdh:all-srcs",
"//vendor/github.com/MakeNowJust/heredoc:all-srcs",
......@@ -74,29 +73,17 @@ filegroup(
"//vendor/github.com/cespare/prettybench:all-srcs",
"//vendor/github.com/chai2010/gettext-go/gettext:all-srcs",
"//vendor/github.com/client9/misspell:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api:all-srcs",
"//vendor/github.com/cloudflare/cfssl/auth:all-srcs",
"//vendor/github.com/cloudflare/cfssl/bundler:all-srcs",
"//vendor/github.com/cloudflare/cfssl/certdb:all-srcs",
"//vendor/github.com/cloudflare/cfssl/certinfo:all-srcs",
"//vendor/github.com/cloudflare/cfssl/cli:all-srcs",
"//vendor/github.com/cloudflare/cfssl/cmd/cfssl:all-srcs",
"//vendor/github.com/cloudflare/cfssl/cmd/cfssljson:all-srcs",
"//vendor/github.com/cloudflare/cfssl/config:all-srcs",
"//vendor/github.com/cloudflare/cfssl/crl:all-srcs",
"//vendor/github.com/cloudflare/cfssl/crypto/pkcs7:all-srcs",
"//vendor/github.com/cloudflare/cfssl/csr:all-srcs",
"//vendor/github.com/cloudflare/cfssl/errors:all-srcs",
"//vendor/github.com/cloudflare/cfssl/helpers:all-srcs",
"//vendor/github.com/cloudflare/cfssl/info:all-srcs",
"//vendor/github.com/cloudflare/cfssl/initca:all-srcs",
"//vendor/github.com/cloudflare/cfssl/log:all-srcs",
"//vendor/github.com/cloudflare/cfssl/ocsp:all-srcs",
"//vendor/github.com/cloudflare/cfssl/revoke:all-srcs",
"//vendor/github.com/cloudflare/cfssl/scan:all-srcs",
"//vendor/github.com/cloudflare/cfssl/selfsign:all-srcs",
"//vendor/github.com/cloudflare/cfssl/ocsp/config:all-srcs",
"//vendor/github.com/cloudflare/cfssl/signer:all-srcs",
"//vendor/github.com/cloudflare/cfssl/ubiquity:all-srcs",
"//vendor/github.com/clusterhq/flocker-go:all-srcs",
"//vendor/github.com/codedellemc/goscaleio:all-srcs",
"//vendor/github.com/container-storage-interface/spec/lib/go/csi:all-srcs",
......@@ -167,7 +154,6 @@ filegroup(
"//vendor/github.com/cyphar/filepath-securejoin:all-srcs",
"//vendor/github.com/d2g/dhcp4:all-srcs",
"//vendor/github.com/d2g/dhcp4client:all-srcs",
"//vendor/github.com/daaku/go.zipexe:all-srcs",
"//vendor/github.com/davecgh/go-spew/spew:all-srcs",
"//vendor/github.com/daviddengcn/go-colortext:all-srcs",
"//vendor/github.com/dgrijalva/jwt-go:all-srcs",
......@@ -211,7 +197,6 @@ filegroup(
"//vendor/github.com/go-openapi/swag:all-srcs",
"//vendor/github.com/go-openapi/validate:all-srcs",
"//vendor/github.com/go-ozzo/ozzo-validation:all-srcs",
"//vendor/github.com/go-sql-driver/mysql:all-srcs",
"//vendor/github.com/godbus/dbus:all-srcs",
"//vendor/github.com/gogo/protobuf/gogoproto:all-srcs",
"//vendor/github.com/gogo/protobuf/plugin/compare:all-srcs",
......@@ -285,18 +270,14 @@ filegroup(
"//vendor/github.com/imdario/mergo:all-srcs",
"//vendor/github.com/inconshreveable/mousetrap:all-srcs",
"//vendor/github.com/jmespath/go-jmespath:all-srcs",
"//vendor/github.com/jmhodges/clock:all-srcs",
"//vendor/github.com/jmoiron/sqlx:all-srcs",
"//vendor/github.com/jonboulle/clockwork:all-srcs",
"//vendor/github.com/json-iterator/go:all-srcs",
"//vendor/github.com/jstemmer/go-junit-report:all-srcs",
"//vendor/github.com/jteeuwen/go-bindata:all-srcs",
"//vendor/github.com/kardianos/osext:all-srcs",
"//vendor/github.com/karrick/godirwalk:all-srcs",
"//vendor/github.com/kisielk/sqlstruct:all-srcs",
"//vendor/github.com/konsorten/go-windows-terminal-sequences:all-srcs",
"//vendor/github.com/kr/fs:all-srcs",
"//vendor/github.com/lib/pq:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/api:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/pkg/parser:all-srcs",
"//vendor/github.com/libopenstorage/openstorage/pkg/units:all-srcs",
......@@ -309,7 +290,6 @@ filegroup(
"//vendor/github.com/mailru/easyjson/jwriter:all-srcs",
"//vendor/github.com/marstr/guid:all-srcs",
"//vendor/github.com/mattn/go-shellwords:all-srcs",
"//vendor/github.com/mattn/go-sqlite3:all-srcs",
"//vendor/github.com/matttproud/golang_protobuf_extensions/pbutil:all-srcs",
"//vendor/github.com/mholt/caddy/caddyfile:all-srcs",
"//vendor/github.com/miekg/dns:all-srcs",
......
/example/example
/example/example.exe
/rice/rice
/rice/rice.exe
*.rice-box.go
*.rice-box.syso
.wercker
language: go
go:
- master
- 1.x.x
- 1.8.x
- 1.7.x
- 1.6.x
- 1.5.x
install:
- go get -t ./...
- env
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then go get github.com/golang/lint/golint; fi
script:
- go build -x ./...
- go test -cover ./...
- go vet ./...
- if [ "${TRAVIS_GO_VERSION%.*}" != "1.5" ]; then golint .; fi
Geert-Johan Riemer <geertjohan@geertjohan.net>
Paul Maddox <paul.maddox@gmail.com>
Vincent Petithory <vincent.petithory@gmail.com>
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"appended.go",
"box.go",
"config.go",
"debug.go",
"embedded.go",
"file.go",
"http.go",
"sort.go",
"virtual.go",
"walk.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/GeertJohan/go.rice",
importpath = "github.com/GeertJohan/go.rice",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/GeertJohan/go.rice/embedded:go_default_library",
"//vendor/github.com/daaku/go.zipexe:go_default_library",
"//vendor/github.com/kardianos/osext:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/GeertJohan/go.rice/embedded:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
Copyright (c) 2013, Geert-Johan Riemer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
## go.rice
[![Build Status](https://travis-ci.org/GeertJohan/go.rice.png)](https://travis-ci.org/GeertJohan/go.rice)
[![Godoc](https://img.shields.io/badge/godoc-go.rice-blue.svg?style=flat-square)](https://godoc.org/github.com/GeertJohan/go.rice)
go.rice is a [Go](http://golang.org) package that makes working with resources such as html,js,css,images and templates very easy. During development `go.rice` will load required files directly from disk. Upon deployment it is easy to add all resource files to a executable using the `rice` tool, without changing the source code for your package. go.rice provides several methods to add resources to a binary.
### What does it do?
The first thing go.rice does is finding the correct absolute path for your resource files. Say you are executing go binary in your home directory, but your `html-files` are located in `$GOPATH/src/yourApplication/html-files`. `go.rice` will lookup the correct path for that directory (relative to the location of yourApplication). The only thing you have to do is include the resources using `rice.FindBox("html-files")`.
This only works when the source is available to the machine executing the binary. Which is always the case when the binary was installed with `go get` or `go install`. It might occur that you wish to simply provide a binary, without source. The `rice` tool analyses source code and finds call's to `rice.FindBox(..)` and adds the required directories to the executable binary. There are several methods to add these resources. You can 'embed' by generating go source code, or append the resource to the executable as zip file. In both cases `go.rice` will detect the embedded or appended resources and load those, instead of looking up files from disk.
### Installation
Use `go get` to install the package the `rice` tool.
```
go get github.com/GeertJohan/go.rice
go get github.com/GeertJohan/go.rice/rice
```
### Package usage
Import the package: `import "github.com/GeertJohan/go.rice"`
**Serving a static content folder over HTTP with a rice Box**
```go
http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
http.ListenAndServe(":8080", nil)
```
**Service a static content folder over HTTP at a non-root location**
```go
box := rice.MustFindBox("cssfiles")
cssFileServer := http.StripPrefix("/css/", http.FileServer(box.HTTPBox()))
http.Handle("/css/", cssFileServer)
http.ListenAndServe(":8080", nil)
```
Note the *trailing slash* in `/css/` in both the call to
`http.StripPrefix` and `http.Handle`.
**Loading a template**
```go
// find a rice.Box
templateBox, err := rice.FindBox("example-templates")
if err != nil {
log.Fatal(err)
}
// get file contents as string
templateString, err := templateBox.String("message.tmpl")
if err != nil {
log.Fatal(err)
}
// parse and execute the template
tmplMessage, err := template.New("message").Parse(templateString)
if err != nil {
log.Fatal(err)
}
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
```
Never call `FindBox()` or `MustFindBox()` from an `init()` function, as the boxes might have not been loaded at that time.
### Tool usage
The `rice` tool lets you add the resources to a binary executable so the files are not loaded from the filesystem anymore. This creates a 'standalone' executable. There are several ways to add the resources to a binary, each has pro's and con's but all will work without requiring changes to the way you load the resources.
#### embed-go
**Embed resources by generating Go source code**
This method must be executed before building. It generates a single Go source file called *rice-box.go* for each package, that is compiled by the go compiler into the binary.
The downside with this option is that the generated go source files can become very large, which will slow down compilation and require lots of memory to compile.
Execute the following commands:
```
rice embed-go
go build
```
*A Note on Symbolic Links*: `embed-go` uses the `os.Walk` function
from the standard library. The `os.Walk` function does **not** follow
symbolic links. So, when creating a box, be aware that any symbolic
links inside your box's directory will not be followed. **However**,
if the box itself is a symbolic link, its actual location will be
resolved first and then walked. In summary, if your box location is a
symbolic link, it will be followed but none of the symbolic links in
the box will be followed.
#### embed-syso
**Embed resources by generating a coff .syso file and some .go source code**
** This method is experimental and should not be used for production systems just yet **
This method must be executed before building. It generates a COFF .syso file and Go source file that are compiled by the go compiler into the binary.
Execute the following commands:
```
rice embed-syso
go build
```
#### append
**Append resources to executable as zip file**
This method changes an already built executable. It appends the resources as zip file to the binary. It makes compilation a lot faster and can be used with large resource files.
Downsides for appending are that it requires `zip` to be installed and does not provide a working Seek method.
Run the following commands to create a standalone executable.
```
go build -o example
rice append --exec example
```
**Note: requires zip command to be installed**
On windows, install zip from http://gnuwin32.sourceforge.net/packages/zip.htm or cygwin/msys toolsets.
#### Help information
Run `rice -h` for information about all options.
You can run the -h option for each sub-command, e.g. `rice append -h`.
### Order of precedence
When opening a new box, the rice package tries to locate the resources in the following order:
- embedded in generated go source
- appended as zip
- 'live' from filesystem
### License
This project is licensed under a Simplified BSD license. Please read the [LICENSE file][license].
### TODO & Development
This package is not completed yet. Though it already provides working embedding, some important featuers are still missing.
- implement Readdir() correctly on virtualDir
- in-code TODO's
- find boxes in imported packages
Less important stuff:
- idea, os/arch dependent embeds. rice checks if embedding file has _os_arch or build flags. If box is not requested by file without buildflags, then the buildflags are applied to the embed file.
### Package documentation
You will find package documentation at [godoc.org/github.com/GeertJohan/go.rice][godoc].
[license]: https://github.com/GeertJohan/go.rice/blob/master/LICENSE
[godoc]: http://godoc.org/github.com/GeertJohan/go.rice
package rice
import (
"archive/zip"
"log"
"os"
"path/filepath"
"strings"
"time"
"github.com/daaku/go.zipexe"
"github.com/kardianos/osext"
)
// appendedBox defines an appended box
type appendedBox struct {
Name string // box name
Files map[string]*appendedFile // appended files (*zip.File) by full path
}
type appendedFile struct {
zipFile *zip.File
dir bool
dirInfo *appendedDirInfo
children []*appendedFile
content []byte
}
// appendedBoxes is a public register of appendes boxes
var appendedBoxes = make(map[string]*appendedBox)
func init() {
// find if exec is appended
thisFile, err := osext.Executable()
if err != nil {
return // not appended or cant find self executable
}
closer, rd, err := zipexe.OpenCloser(thisFile)
if err != nil {
return // not appended
}
defer closer.Close()
for _, f := range rd.File {
// get box and file name from f.Name
fileParts := strings.SplitN(strings.TrimLeft(filepath.ToSlash(f.Name), "/"), "/", 2)
boxName := fileParts[0]
var fileName string
if len(fileParts) > 1 {
fileName = fileParts[1]
}
// find box or create new one if doesn't exist
box := appendedBoxes[boxName]
if box == nil {
box = &appendedBox{
Name: boxName,
Files: make(map[string]*appendedFile),
}
appendedBoxes[boxName] = box
}
// create and add file to box
af := &appendedFile{
zipFile: f,
}
if f.Comment == "dir" {
af.dir = true
af.dirInfo = &appendedDirInfo{
name: filepath.Base(af.zipFile.Name),
//++ TODO: use zip modtime when that is set correctly: af.zipFile.ModTime()
time: time.Now(),
}
} else {
// this is a file, we need it's contents so we can create a bytes.Reader when the file is opened
// make a new byteslice
af.content = make([]byte, af.zipFile.FileInfo().Size())
// ignore reading empty files from zip (empty file still is a valid file to be read though!)
if len(af.content) > 0 {
// open io.ReadCloser
rc, err := af.zipFile.Open()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error opening appended file %s: %v", af.zipFile.Name, err)
} else {
_, err = rc.Read(af.content)
rc.Close()
if err != nil {
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
log.Printf("error reading data for appended file %s: %v", af.zipFile.Name, err)
}
}
}
}
// add appendedFile to box file list
box.Files[fileName] = af
// add to parent dir (if any)
dirName := filepath.Dir(fileName)
if dirName == "." {
dirName = ""
}
if fileName != "" { // don't make box root dir a child of itself
if dir := box.Files[dirName]; dir != nil {
dir.children = append(dir.children, af)
}
}
}
}
// implements os.FileInfo.
// used for Readdir()
type appendedDirInfo struct {
name string
time time.Time
}
func (adi *appendedDirInfo) Name() string {
return adi.name
}
func (adi *appendedDirInfo) Size() int64 {
return 0
}
func (adi *appendedDirInfo) Mode() os.FileMode {
return os.ModeDir
}
func (adi *appendedDirInfo) ModTime() time.Time {
return adi.time
}
func (adi *appendedDirInfo) IsDir() bool {
return true
}
func (adi *appendedDirInfo) Sys() interface{} {
return nil
}
package rice
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/GeertJohan/go.rice/embedded"
)
// Box abstracts a directory for resources/files.
// It can either load files from disk, or from embedded code (when `rice --embed` was ran).
type Box struct {
name string
absolutePath string
embed *embedded.EmbeddedBox
appendd *appendedBox
}
var defaultLocateOrder = []LocateMethod{LocateEmbedded, LocateAppended, LocateFS}
func findBox(name string, order []LocateMethod) (*Box, error) {
b := &Box{name: name}
// no support for absolute paths since gopath can be different on different machines.
// therefore, required box must be located relative to package requiring it.
if filepath.IsAbs(name) {
return nil, errors.New("given name/path is absolute")
}
var err error
for _, method := range order {
switch method {
case LocateEmbedded:
if embed := embedded.EmbeddedBoxes[name]; embed != nil {
b.embed = embed
return b, nil
}
case LocateAppended:
appendedBoxName := strings.Replace(name, `/`, `-`, -1)
if appendd := appendedBoxes[appendedBoxName]; appendd != nil {
b.appendd = appendd
return b, nil
}
case LocateFS:
// resolve absolute directory path
err := b.resolveAbsolutePathFromCaller()
if err != nil {
continue
}
// check if absolutePath exists on filesystem
info, err := os.Stat(b.absolutePath)
if err != nil {
continue
}
// check if absolutePath is actually a directory
if !info.IsDir() {
err = errors.New("given name/path is not a directory")
continue
}
return b, nil
case LocateWorkingDirectory:
// resolve absolute directory path
err := b.resolveAbsolutePathFromWorkingDirectory()
if err != nil {
continue
}
// check if absolutePath exists on filesystem
info, err := os.Stat(b.absolutePath)
if err != nil {
continue
}
// check if absolutePath is actually a directory
if !info.IsDir() {
err = errors.New("given name/path is not a directory")
continue
}
return b, nil
}
}
if err == nil {
err = fmt.Errorf("could not locate box %q", name)
}
return nil, err
}
// FindBox returns a Box instance for given name.
// When the given name is a relative path, it's base path will be the calling pkg/cmd's source root.
// When the given name is absolute, it's absolute. derp.
// Make sure the path doesn't contain any sensitive information as it might be placed into generated go source (embedded).
func FindBox(name string) (*Box, error) {
return findBox(name, defaultLocateOrder)
}
// MustFindBox returns a Box instance for given name, like FindBox does.
// It does not return an error, instead it panics when an error occurs.
func MustFindBox(name string) *Box {
box, err := findBox(name, defaultLocateOrder)
if err != nil {
panic(err)
}
return box
}
// This is injected as a mutable function literal so that we can mock it out in
// tests and return a fixed test file.
var resolveAbsolutePathFromCaller = func(name string, nStackFrames int) (string, error) {
_, callingGoFile, _, ok := runtime.Caller(nStackFrames)
if !ok {
return "", errors.New("couldn't find caller on stack")
}
// resolve to proper path
pkgDir := filepath.Dir(callingGoFile)
// fix for go cover
const coverPath = "_test/_obj_test"
if !filepath.IsAbs(pkgDir) {
if i := strings.Index(pkgDir, coverPath); i >= 0 {
pkgDir = pkgDir[:i] + pkgDir[i+len(coverPath):] // remove coverPath
pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", pkgDir) // make absolute
}
}
return filepath.Join(pkgDir, name), nil
}
func (b *Box) resolveAbsolutePathFromCaller() error {
path, err := resolveAbsolutePathFromCaller(b.name, 4)
if err != nil {
return err
}
b.absolutePath = path
return nil
}
func (b *Box) resolveAbsolutePathFromWorkingDirectory() error {
path, err := os.Getwd()
if err != nil {
return err
}
b.absolutePath = filepath.Join(path, b.name)
return nil
}
// IsEmbedded indicates wether this box was embedded into the application
func (b *Box) IsEmbedded() bool {
return b.embed != nil
}
// IsAppended indicates wether this box was appended to the application
func (b *Box) IsAppended() bool {
return b.appendd != nil
}
// Time returns how actual the box is.
// When the box is embedded, it's value is saved in the embedding code.
// When the box is live, this methods returns time.Now()
func (b *Box) Time() time.Time {
if b.IsEmbedded() {
return b.embed.Time
}
//++ TODO: return time for appended box
return time.Now()
}
// Open opens a File from the box
// If there is an error, it will be of type *os.PathError.
func (b *Box) Open(name string) (*File, error) {
if Debug {
fmt.Printf("Open(%s)\n", name)
}
if b.IsEmbedded() {
if Debug {
fmt.Println("Box is embedded")
}
// trim prefix (paths are relative to box)
name = strings.TrimLeft(name, "/")
if Debug {
fmt.Printf("Trying %s\n", name)
}
// search for file
ef := b.embed.Files[name]
if ef == nil {
if Debug {
fmt.Println("Didn't find file in embed")
}
// file not found, try dir
ed := b.embed.Dirs[name]
if ed == nil {
if Debug {
fmt.Println("Didn't find dir in embed")
}
// dir not found, error out
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
if Debug {
fmt.Println("Found dir. Returning virtual dir")
}
vd := newVirtualDir(ed)
return &File{virtualD: vd}, nil
}
// box is embedded
if Debug {
fmt.Println("Found file. Returning virtual file")
}
vf := newVirtualFile(ef)
return &File{virtualF: vf}, nil
}
if b.IsAppended() {
// trim prefix (paths are relative to box)
name = strings.TrimLeft(name, "/")
// search for file
appendedFile := b.appendd.Files[name]
if appendedFile == nil {
return nil, &os.PathError{
Op: "open",
Path: name,
Err: os.ErrNotExist,
}
}
// create new file
f := &File{
appendedF: appendedFile,
}
// if this file is a directory, we want to be able to read and seek
if !appendedFile.dir {
// looks like malformed data in zip, error now
if appendedFile.content == nil {
return nil, &os.PathError{
Op: "open",
Path: "name",
Err: errors.New("error reading data from zip file"),
}
}
// create new bytes.Reader
f.appendedFileReader = bytes.NewReader(appendedFile.content)
}
// all done
return f, nil
}
// perform os open
if Debug {
fmt.Printf("Using os.Open(%s)", filepath.Join(b.absolutePath, name))
}
file, err := os.Open(filepath.Join(b.absolutePath, name))
if err != nil {
return nil, err
}
return &File{realF: file}, nil
}
// Bytes returns the content of the file with given name as []byte.
func (b *Box) Bytes(name string) ([]byte, error) {
file, err := b.Open(name)
if err != nil {
return nil, err
}
defer file.Close()
content, err := ioutil.ReadAll(file)
if err != nil {
return nil, err
}
return content, nil
}
// MustBytes returns the content of the file with given name as []byte.
// panic's on error.
func (b *Box) MustBytes(name string) []byte {
bts, err := b.Bytes(name)
if err != nil {
panic(err)
}
return bts
}
// String returns the content of the file with given name as string.
func (b *Box) String(name string) (string, error) {
// check if box is embedded, optimized fast path
if b.IsEmbedded() {
// find file in embed
ef := b.embed.Files[name]
if ef == nil {
return "", os.ErrNotExist
}
// return as string
return ef.Content, nil
}
bts, err := b.Bytes(name)
if err != nil {
return "", err
}
return string(bts), nil
}
// MustString returns the content of the file with given name as string.
// panic's on error.
func (b *Box) MustString(name string) string {
str, err := b.String(name)
if err != nil {
panic(err)
}
return str
}
// Name returns the name of the box
func (b *Box) Name() string {
return b.name
}
package rice
// LocateMethod defines how a box is located.
type LocateMethod int
const (
LocateFS = LocateMethod(iota) // Locate on the filesystem according to package path.
LocateAppended // Locate boxes appended to the executable.
LocateEmbedded // Locate embedded boxes.
LocateWorkingDirectory // Locate on the binary working directory
)
// Config allows customizing the box lookup behavior.
type Config struct {
// LocateOrder defines the priority order that boxes are searched for. By
// default, the package global FindBox searches for embedded boxes first,
// then appended boxes, and then finally boxes on the filesystem. That
// search order may be customized by provided the ordered list here. Leaving
// out a particular method will omit that from the search space. For
// example, []LocateMethod{LocateEmbedded, LocateAppended} will never search
// the filesystem for boxes.
LocateOrder []LocateMethod
}
// FindBox searches for boxes using the LocateOrder of the config.
func (c *Config) FindBox(boxName string) (*Box, error) {
return findBox(boxName, c.LocateOrder)
}
// MustFindBox searches for boxes using the LocateOrder of the config, like
// FindBox does. It does not return an error, instead it panics when an error
// occurs.
func (c *Config) MustFindBox(boxName string) *Box {
box, err := findBox(boxName, c.LocateOrder)
if err != nil {
panic(err)
}
return box
}
package rice
// Debug can be set to true to enable debugging.
var Debug = false
package rice
import (
"os"
"time"
"github.com/GeertJohan/go.rice/embedded"
)
// re-type to make exported methods invisible to user (godoc)
// they're not required for the user
// embeddedDirInfo implements os.FileInfo
type embeddedDirInfo embedded.EmbeddedDir
// Name returns the base name of the directory
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Name() string {
return ed.Filename
}
// Size always returns 0
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Size() int64 {
return 0
}
// Mode returns the file mode bits
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Mode() os.FileMode {
return os.FileMode(0555 | os.ModeDir) // dr-xr-xr-x
}
// ModTime returns the modification time
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) ModTime() time.Time {
return ed.DirModTime
}
// IsDir returns the abbreviation for Mode().IsDir() (always true)
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) IsDir() bool {
return true
}
// Sys returns the underlying data source (always nil)
// (implementing os.FileInfo)
func (ed *embeddedDirInfo) Sys() interface{} {
return nil
}
// re-type to make exported methods invisible to user (godoc)
// they're not required for the user
// embeddedFileInfo implements os.FileInfo
type embeddedFileInfo embedded.EmbeddedFile
// Name returns the base name of the file
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Name() string {
return ef.Filename
}
// Size returns the length in bytes for regular files; system-dependent for others
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Size() int64 {
return int64(len(ef.Content))
}
// Mode returns the file mode bits
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Mode() os.FileMode {
return os.FileMode(0555) // r-xr-xr-x
}
// ModTime returns the modification time
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) ModTime() time.Time {
return ef.FileModTime
}
// IsDir returns the abbreviation for Mode().IsDir() (always false)
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) IsDir() bool {
return false
}
// Sys returns the underlying data source (always nil)
// (implementing os.FileInfo)
func (ef *embeddedFileInfo) Sys() interface{} {
return nil
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["embedded.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/GeertJohan/go.rice/embedded",
importpath = "github.com/GeertJohan/go.rice/embedded",
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
package embedded
import (
"fmt"
"path/filepath"
"strings"
"time"
)
const (
EmbedTypeGo = 0
EmbedTypeSyso = 1
)
// EmbeddedBox defines an embedded box
type EmbeddedBox struct {
Name string // box name
Time time.Time // embed time
EmbedType int // kind of embedding
Files map[string]*EmbeddedFile // ALL embedded files by full path
Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path
}
// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
func (e *EmbeddedBox) Link() {
for path, ed := range e.Dirs {
fmt.Println(path)
ed.ChildDirs = make([]*EmbeddedDir, 0)
ed.ChildFiles = make([]*EmbeddedFile, 0)
}
for path, ed := range e.Dirs {
parentDirpath, _ := filepath.Split(path)
if strings.HasSuffix(parentDirpath, "/") {
parentDirpath = parentDirpath[:len(parentDirpath)-1]
}
parentDir := e.Dirs[parentDirpath]
if parentDir == nil {
panic("parentDir `" + parentDirpath + "` is missing in embedded box")
}
parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
}
for path, ef := range e.Files {
dirpath, _ := filepath.Split(path)
if strings.HasSuffix(dirpath, "/") {
dirpath = dirpath[:len(dirpath)-1]
}
dir := e.Dirs[dirpath]
if dir == nil {
panic("dir `" + dirpath + "` is missing in embedded box")
}
dir.ChildFiles = append(dir.ChildFiles, ef)
}
}
// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedDir struct {
Filename string
DirModTime time.Time
ChildDirs []*EmbeddedDir // direct childs, as returned by virtualDir.Readdir()
ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir()
}
// EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
type EmbeddedFile struct {
Filename string // filename
FileModTime time.Time
Content string
}
// EmbeddedBoxes is a public register of embedded boxes
var EmbeddedBoxes = make(map[string]*EmbeddedBox)
// RegisterEmbeddedBox registers an EmbeddedBox
func RegisterEmbeddedBox(name string, box *EmbeddedBox) {
if _, exists := EmbeddedBoxes[name]; exists {
panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name))
}
EmbeddedBoxes[name] = box
}
package rice
import (
"bytes"
"errors"
"os"
"path/filepath"
)
// File implements the io.Reader, io.Seeker, io.Closer and http.File interfaces
type File struct {
// File abstracts file methods so the user doesn't see the difference between rice.virtualFile, rice.virtualDir and os.File
// TODO: maybe use internal File interface and four implementations: *os.File, appendedFile, virtualFile, virtualDir
// real file on disk
realF *os.File
// when embedded (go)
virtualF *virtualFile
virtualD *virtualDir
// when appended (zip)
appendedF *appendedFile
appendedFileReader *bytes.Reader
// TODO: is appendedFileReader subject of races? Might need a lock here..
}
// Close is like (*os.File).Close()
// Visit http://golang.org/pkg/os/#File.Close for more information
func (f *File) Close() error {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return errors.New("already closed")
}
f.appendedFileReader = nil
return nil
}
if f.virtualF != nil {
return f.virtualF.close()
}
if f.virtualD != nil {
return f.virtualD.close()
}
return f.realF.Close()
}
// Stat is like (*os.File).Stat()
// Visit http://golang.org/pkg/os/#File.Stat for more information
func (f *File) Stat() (os.FileInfo, error) {
if f.appendedF != nil {
if f.appendedF.dir {
return f.appendedF.dirInfo, nil
}
if f.appendedFileReader == nil {
return nil, errors.New("file is closed")
}
return f.appendedF.zipFile.FileInfo(), nil
}
if f.virtualF != nil {
return f.virtualF.stat()
}
if f.virtualD != nil {
return f.virtualD.stat()
}
return f.realF.Stat()
}
// Readdir is like (*os.File).Readdir()
// Visit http://golang.org/pkg/os/#File.Readdir for more information
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
if f.appendedF != nil {
if f.appendedF.dir {
fi := make([]os.FileInfo, 0, len(f.appendedF.children))
for _, childAppendedFile := range f.appendedF.children {
if childAppendedFile.dir {
fi = append(fi, childAppendedFile.dirInfo)
} else {
fi = append(fi, childAppendedFile.zipFile.FileInfo())
}
}
return fi, nil
}
//++ TODO: is os.ErrInvalid the correct error for Readdir on file?
return nil, os.ErrInvalid
}
if f.virtualF != nil {
return f.virtualF.readdir(count)
}
if f.virtualD != nil {
return f.virtualD.readdir(count)
}
return f.realF.Readdir(count)
}
// Read is like (*os.File).Read()
// Visit http://golang.org/pkg/os/#File.Read for more information
func (f *File) Read(bts []byte) (int, error) {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return 0, &os.PathError{
Op: "read",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("file is closed"),
}
}
if f.appendedF.dir {
return 0, &os.PathError{
Op: "read",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("is a directory"),
}
}
return f.appendedFileReader.Read(bts)
}
if f.virtualF != nil {
return f.virtualF.read(bts)
}
if f.virtualD != nil {
return f.virtualD.read(bts)
}
return f.realF.Read(bts)
}
// Seek is like (*os.File).Seek()
// Visit http://golang.org/pkg/os/#File.Seek for more information
func (f *File) Seek(offset int64, whence int) (int64, error) {
if f.appendedF != nil {
if f.appendedFileReader == nil {
return 0, &os.PathError{
Op: "seek",
Path: filepath.Base(f.appendedF.zipFile.Name),
Err: errors.New("file is closed"),
}
}
return f.appendedFileReader.Seek(offset, whence)
}
if f.virtualF != nil {
return f.virtualF.seek(offset, whence)
}
if f.virtualD != nil {
return f.virtualD.seek(offset, whence)
}
return f.realF.Seek(offset, whence)
}
package rice
import (
"net/http"
)
// HTTPBox implements http.FileSystem which allows the use of Box with a http.FileServer.
// e.g.: http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
type HTTPBox struct {
*Box
}
// HTTPBox creates a new HTTPBox from an existing Box
func (b *Box) HTTPBox() *HTTPBox {
return &HTTPBox{b}
}
// Open returns a File using the http.File interface
func (hb *HTTPBox) Open(name string) (http.File, error) {
return hb.Box.Open(name)
}
package rice
import "os"
// SortByName allows an array of os.FileInfo objects
// to be easily sorted by filename using sort.Sort(SortByName(array))
type SortByName []os.FileInfo
func (f SortByName) Len() int { return len(f) }
func (f SortByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f SortByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
// SortByModified allows an array of os.FileInfo objects
// to be easily sorted by modified date using sort.Sort(SortByModified(array))
type SortByModified []os.FileInfo
func (f SortByModified) Len() int { return len(f) }
func (f SortByModified) Less(i, j int) bool { return f[i].ModTime().Unix() > f[j].ModTime().Unix() }
func (f SortByModified) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
package rice
import (
"errors"
"io"
"os"
"path/filepath"
"sort"
"github.com/GeertJohan/go.rice/embedded"
)
//++ TODO: IDEA: merge virtualFile and virtualDir, this decreases work done by rice.File
// Error indicating some function is not implemented yet (but available to satisfy an interface)
var ErrNotImplemented = errors.New("not implemented yet")
// virtualFile is a 'stateful' virtual file.
// virtualFile wraps an *EmbeddedFile for a call to Box.Open() and virtualizes 'read cursor' (offset) and 'closing'.
// virtualFile is only internally visible and should be exposed through rice.File
type virtualFile struct {
*embedded.EmbeddedFile // the actual embedded file, embedded to obtain methods
offset int64 // read position on the virtual file
closed bool // closed when true
}
// create a new virtualFile for given EmbeddedFile
func newVirtualFile(ef *embedded.EmbeddedFile) *virtualFile {
vf := &virtualFile{
EmbeddedFile: ef,
offset: 0,
closed: false,
}
return vf
}
//++ TODO check for nil pointers in all these methods. When so: return os.PathError with Err: os.ErrInvalid
func (vf *virtualFile) close() error {
if vf.closed {
return &os.PathError{
Op: "close",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("already closed"),
}
}
vf.EmbeddedFile = nil
vf.closed = true
return nil
}
func (vf *virtualFile) stat() (os.FileInfo, error) {
if vf.closed {
return nil, &os.PathError{
Op: "stat",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
return (*embeddedFileInfo)(vf.EmbeddedFile), nil
}
func (vf *virtualFile) readdir(count int) ([]os.FileInfo, error) {
if vf.closed {
return nil, &os.PathError{
Op: "readdir",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
//TODO: return proper error for a readdir() call on a file
return nil, ErrNotImplemented
}
func (vf *virtualFile) read(bts []byte) (int, error) {
if vf.closed {
return 0, &os.PathError{
Op: "read",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
end := vf.offset + int64(len(bts))
if end >= int64(len(vf.Content)) {
// end of file, so return what we have + EOF
n := copy(bts, vf.Content[vf.offset:])
vf.offset = 0
return n, io.EOF
}
n := copy(bts, vf.Content[vf.offset:end])
vf.offset += int64(n)
return n, nil
}
func (vf *virtualFile) seek(offset int64, whence int) (int64, error) {
if vf.closed {
return 0, &os.PathError{
Op: "seek",
Path: vf.EmbeddedFile.Filename,
Err: errors.New("bad file descriptor"),
}
}
var e error
//++ TODO: check if this is correct implementation for seek
switch whence {
case os.SEEK_SET:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset = offset
case os.SEEK_CUR:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset += offset
case os.SEEK_END:
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
vf.offset = int64(len(vf.EmbeddedFile.Content)) - offset
}
if e != nil {
return 0, &os.PathError{
Op: "seek",
Path: vf.Filename,
Err: e,
}
}
return vf.offset, nil
}
// virtualDir is a 'stateful' virtual directory.
// virtualDir wraps an *EmbeddedDir for a call to Box.Open() and virtualizes 'closing'.
// virtualDir is only internally visible and should be exposed through rice.File
type virtualDir struct {
*embedded.EmbeddedDir
offset int // readdir position on the directory
closed bool
}
// create a new virtualDir for given EmbeddedDir
func newVirtualDir(ed *embedded.EmbeddedDir) *virtualDir {
vd := &virtualDir{
EmbeddedDir: ed,
offset: 0,
closed: false,
}
return vd
}
func (vd *virtualDir) close() error {
//++ TODO: needs sync mutex?
if vd.closed {
return &os.PathError{
Op: "close",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("already closed"),
}
}
vd.closed = true
return nil
}
func (vd *virtualDir) stat() (os.FileInfo, error) {
if vd.closed {
return nil, &os.PathError{
Op: "stat",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return (*embeddedDirInfo)(vd.EmbeddedDir), nil
}
func (vd *virtualDir) readdir(n int) (fi []os.FileInfo, err error) {
if vd.closed {
return nil, &os.PathError{
Op: "readdir",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
// Build up the array of our contents
var files []os.FileInfo
// Add the child directories
for _, child := range vd.ChildDirs {
child.Filename = filepath.Base(child.Filename)
files = append(files, (*embeddedDirInfo)(child))
}
// Add the child files
for _, child := range vd.ChildFiles {
child.Filename = filepath.Base(child.Filename)
files = append(files, (*embeddedFileInfo)(child))
}
// Sort it by filename (lexical order)
sort.Sort(SortByName(files))
// Return all contents if that's what is requested
if n <= 0 {
vd.offset = 0
return files, nil
}
// If user has requested past the end of our list
// return what we can and send an EOF
if vd.offset+n >= len(files) {
offset := vd.offset
vd.offset = 0
return files[offset:], io.EOF
}
offset := vd.offset
vd.offset += n
return files[offset : offset+n], nil
}
func (vd *virtualDir) read(bts []byte) (int, error) {
if vd.closed {
return 0, &os.PathError{
Op: "read",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return 0, &os.PathError{
Op: "read",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("is a directory"),
}
}
func (vd *virtualDir) seek(offset int64, whence int) (int64, error) {
if vd.closed {
return 0, &os.PathError{
Op: "seek",
Path: vd.EmbeddedDir.Filename,
Err: errors.New("bad file descriptor"),
}
}
return 0, &os.PathError{
Op: "seek",
Path: vd.Filename,
Err: errors.New("is a directory"),
}
}
package rice
import (
"os"
"path/filepath"
"sort"
"strings"
)
// Walk is like filepath.Walk()
// Visit http://golang.org/pkg/path/filepath/#Walk for more information
func (b *Box) Walk(path string, walkFn filepath.WalkFunc) error {
pathFile, err := b.Open(path)
if err != nil {
return err
}
defer pathFile.Close()
pathInfo, err := pathFile.Stat()
if err != nil {
return err
}
if b.IsAppended() || b.IsEmbedded() {
return b.walk(path, pathInfo, walkFn)
}
// We don't have any embedded or appended box so use live filesystem mode
return filepath.Walk(b.absolutePath+string(os.PathSeparator)+path, func(path string, info os.FileInfo, err error) error {
// Strip out the box name from the returned paths
path = strings.TrimPrefix(path, b.absolutePath+string(os.PathSeparator))
return walkFn(path, info, err)
})
}
// walk recursively descends path.
// See walk() in $GOROOT/src/pkg/path/filepath/path.go
func (b *Box) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := b.readDirNames(path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := filepath.Join(path, name)
fileObject, err := b.Open(filename)
if err != nil {
return err
}
defer fileObject.Close()
fileInfo, err := fileObject.Stat()
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = b.walk(filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// readDirNames reads the directory named by path and returns a sorted list of directory entries.
// See readDirNames() in $GOROOT/pkg/path/filepath/path.go
func (b *Box) readDirNames(path string) ([]string, error) {
f, err := b.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return nil, err
}
if !stat.IsDir() {
return nil, nil
}
infos, err := f.Readdir(0)
if err != nil {
return nil, err
}
var names []string
for _, info := range infos {
names = append(names, info.Name())
}
sort.Strings(names)
return names, nil
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["api.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api",
importpath = "github.com/cloudflare/cfssl/api",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/cloudflare/cfssl/api/bundle:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/certinfo:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/client:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/crl:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/gencrl:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/generator:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/health:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/info:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/initca:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/ocsp:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/revoke:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/scan:all-srcs",
"//vendor/github.com/cloudflare/cfssl/api/signhandler:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package api implements an HTTP-based API and server for CFSSL.
package api
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
)
// Handler is an interface providing a generic mechanism for handling HTTP requests.
type Handler interface {
Handle(w http.ResponseWriter, r *http.Request) error
}
// HTTPHandler is a wrapper that encapsulates Handler interface as http.Handler.
// HTTPHandler also enforces that the Handler only responds to requests with registered HTTP methods.
type HTTPHandler struct {
Handler // CFSSL handler
Methods []string // The associated HTTP methods
}
// HandlerFunc is similar to the http.HandlerFunc type; it serves as
// an adapter allowing the use of ordinary functions as Handlers. If
// f is a function with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(http.ResponseWriter, *http.Request) error
// Handle calls f(w, r)
func (f HandlerFunc) Handle(w http.ResponseWriter, r *http.Request) error {
w.Header().Set("Content-Type", "application/json")
return f(w, r)
}
// HandleError is the centralised error handling and reporting.
func HandleError(w http.ResponseWriter, err error) (code int) {
if err == nil {
return http.StatusOK
}
msg := err.Error()
httpCode := http.StatusInternalServerError
// If it is recognized as HttpError emitted from cfssl,
// we rewrite the status code accordingly. If it is a
// cfssl error, set the http status to StatusBadRequest
switch err := err.(type) {
case *errors.HTTPError:
httpCode = err.StatusCode
code = err.StatusCode
case *errors.Error:
httpCode = http.StatusBadRequest
code = err.ErrorCode
msg = err.Message
}
response := NewErrorResponse(msg, code)
jsonMessage, err := json.Marshal(response)
if err != nil {
log.Errorf("Failed to marshal JSON: %v", err)
} else {
msg = string(jsonMessage)
}
http.Error(w, msg, httpCode)
return code
}
// ServeHTTP encapsulates the call to underlying Handler to handle the request
// and return the response with proper HTTP status code
func (h HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var err error
var match bool
// Throw 405 when requested with an unsupported verb.
for _, m := range h.Methods {
if m == r.Method {
match = true
}
}
if match {
err = h.Handle(w, r)
} else {
err = errors.NewMethodNotAllowed(r.Method)
}
status := HandleError(w, err)
log.Infof("%s - \"%s %s\" %d", r.RemoteAddr, r.Method, r.URL, status)
}
// readRequestBlob takes a JSON-blob-encoded response body in the form
// map[string]string and returns it, the list of keywords presented,
// and any error that occurred.
func readRequestBlob(r *http.Request) (map[string]string, error) {
var blob map[string]string
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
r.Body.Close()
err = json.Unmarshal(body, &blob)
if err != nil {
return nil, err
}
return blob, nil
}
// ProcessRequestOneOf reads a JSON blob for the request and makes
// sure it contains one of a set of keywords. For example, a request
// might have the ('foo' && 'bar') keys, OR it might have the 'baz'
// key. In either case, we want to accept the request; however, if
// none of these sets shows up, the request is a bad request, and it
// should be returned.
func ProcessRequestOneOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
blob, err := readRequestBlob(r)
if err != nil {
return nil, nil, err
}
var matched []string
for _, set := range keywordSets {
if matchKeywords(blob, set) {
if matched != nil {
return nil, nil, errors.NewBadRequestString("mismatched parameters")
}
matched = set
}
}
if matched == nil {
return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
}
return blob, matched, nil
}
// ProcessRequestFirstMatchOf reads a JSON blob for the request and returns
// the first match of a set of keywords. For example, a request
// might have one of the following combinations: (foo=1, bar=2), (foo=1), and (bar=2)
// By giving a specific ordering of those combinations, we could decide how to accept
// the request.
func ProcessRequestFirstMatchOf(r *http.Request, keywordSets [][]string) (map[string]string, []string, error) {
blob, err := readRequestBlob(r)
if err != nil {
return nil, nil, err
}
for _, set := range keywordSets {
if matchKeywords(blob, set) {
return blob, set, nil
}
}
return nil, nil, errors.NewBadRequestString("no valid parameter sets found")
}
func matchKeywords(blob map[string]string, keywords []string) bool {
for _, keyword := range keywords {
if _, ok := blob[keyword]; !ok {
return false
}
}
return true
}
// ResponseMessage implements the standard for response errors and
// messages. A message has a code and a string message.
type ResponseMessage struct {
Code int `json:"code"`
Message string `json:"message"`
}
// Response implements the CloudFlare standard for API
// responses.
type Response struct {
Success bool `json:"success"`
Result interface{} `json:"result"`
Errors []ResponseMessage `json:"errors"`
Messages []ResponseMessage `json:"messages"`
}
// NewSuccessResponse is a shortcut for creating new successul API
// responses.
func NewSuccessResponse(result interface{}) Response {
return Response{
Success: true,
Result: result,
Errors: []ResponseMessage{},
Messages: []ResponseMessage{},
}
}
// NewSuccessResponseWithMessage is a shortcut for creating new successul API
// responses that includes a message.
func NewSuccessResponseWithMessage(result interface{}, message string, code int) Response {
return Response{
Success: true,
Result: result,
Errors: []ResponseMessage{},
Messages: []ResponseMessage{{code, message}},
}
}
// NewErrorResponse is a shortcut for creating an error response for a
// single error.
func NewErrorResponse(message string, code int) Response {
return Response{
Success: false,
Result: nil,
Errors: []ResponseMessage{{code, message}},
Messages: []ResponseMessage{},
}
}
// SendResponse builds a response from the result, sets the JSON
// header, and writes to the http.ResponseWriter.
func SendResponse(w http.ResponseWriter, result interface{}) error {
response := NewSuccessResponse(result)
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
err := enc.Encode(response)
return err
}
// SendResponseWithMessage builds a response from the result and the
// provided message, sets the JSON header, and writes to the
// http.ResponseWriter.
func SendResponseWithMessage(w http.ResponseWriter, result interface{}, message string, code int) error {
response := NewSuccessResponseWithMessage(result, message, code)
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
err := enc.Encode(response)
return err
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["bundle.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/bundle",
importpath = "github.com/cloudflare/cfssl/api/bundle",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/bundler:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package bundle implements the HTTP handler for the bundle command.
package bundle
import (
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/bundler"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
)
// Handler accepts requests for either remote or uploaded
// certificates to be bundled, and returns a certificate bundle (or
// error).
type Handler struct {
bundler *bundler.Bundler
}
// NewHandler creates a new bundler that uses the root bundle and
// intermediate bundle in the trust chain.
func NewHandler(caBundleFile, intBundleFile string) (http.Handler, error) {
var err error
b := new(Handler)
if b.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile); err != nil {
return nil, err
}
log.Info("bundler API ready")
return api.HTTPHandler{Handler: b, Methods: []string{"POST"}}, nil
}
// Handle implements an http.Handler interface for the bundle handler.
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
blob, matched, err := api.ProcessRequestFirstMatchOf(r,
[][]string{
{"certificate"},
{"domain"},
})
if err != nil {
log.Warningf("invalid request: %v", err)
return err
}
flavor := blob["flavor"]
bf := bundler.Ubiquitous
if flavor != "" {
bf = bundler.BundleFlavor(flavor)
}
log.Infof("request for flavor %v", bf)
var result *bundler.Bundle
switch matched[0] {
case "domain":
bundle, err := h.bundler.BundleFromRemote(blob["domain"], blob["ip"], bf)
if err != nil {
log.Warningf("couldn't bundle from remote: %v", err)
return err
}
result = bundle
case "certificate":
bundle, err := h.bundler.BundleFromPEMorDER([]byte(blob["certificate"]), []byte(blob["private_key"]), bf, "")
if err != nil {
log.Warning("bad PEM certifcate or private key")
return err
}
serverName := blob["domain"]
ip := blob["ip"]
if serverName != "" {
err := bundle.Cert.VerifyHostname(serverName)
if err != nil {
return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
}
if ip != "" {
err := bundle.Cert.VerifyHostname(ip)
if err != nil {
return errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
}
}
result = bundle
}
log.Info("wrote response")
return api.SendResponse(w, result)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["certinfo.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/certinfo",
importpath = "github.com/cloudflare/cfssl/api/certinfo",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/certinfo:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package certinfo implements the HTTP handler for the certinfo command.
package certinfo
import (
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/certinfo"
"github.com/cloudflare/cfssl/log"
)
// Handler accepts requests for either remote or uploaded
// certificates to be bundled, and returns a certificate bundle (or
// error).
type Handler struct{}
// NewHandler creates a new bundler that uses the root bundle and
// intermediate bundle in the trust chain.
func NewHandler() http.Handler {
return api.HTTPHandler{Handler: new(Handler), Methods: []string{"POST"}}
}
// Handle implements an http.Handler interface for the bundle handler.
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) (err error) {
blob, matched, err := api.ProcessRequestFirstMatchOf(r,
[][]string{
{"certificate"},
{"domain"},
})
if err != nil {
log.Warningf("invalid request: %v", err)
return err
}
var cert *certinfo.Certificate
switch matched[0] {
case "domain":
if cert, err = certinfo.ParseCertificateDomain(blob["domain"]); err != nil {
log.Warningf("couldn't parse remote certificate: %v", err)
return err
}
case "certificate":
if cert, err = certinfo.ParseCertificatePEM([]byte(blob["certificate"])); err != nil {
log.Warningf("bad PEM certifcate: %v", err)
return err
}
}
return api.SendResponse(w, cert)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"api.go",
"client.go",
"group.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/client",
importpath = "github.com/cloudflare/cfssl/api/client",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/auth:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/info:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package client
// SignResult is the result of signing a CSR.
type SignResult struct {
Certificate []byte `json:"certificate"`
}
package client
import (
"crypto/tls"
"errors"
"net/http"
"net/url"
"strings"
"time"
"github.com/cloudflare/cfssl/auth"
"github.com/cloudflare/cfssl/info"
)
// Strategy is the means by which the server to use as a remote should
// be selected.
type Strategy int
const (
// StrategyInvalid indicates any strategy that is unsupported
// or returned when no strategy is applicable.
StrategyInvalid = iota
// StrategyOrderedList is a sequential list of servers: if the
// first server cannot be reached, the next is used. The
// client will proceed in this manner until the list of
// servers is exhausted, and then an error is returned.
StrategyOrderedList
)
var strategyStrings = map[string]Strategy{
"ordered_list": StrategyOrderedList,
}
// StrategyFromString takes a string describing a
func StrategyFromString(s string) Strategy {
s = strings.TrimSpace(strings.ToLower(s))
strategy, ok := strategyStrings[s]
if !ok {
return StrategyInvalid
}
return strategy
}
// NewGroup will use the collection of remotes specified with the
// given strategy.
func NewGroup(remotes []string, tlsConfig *tls.Config, strategy Strategy) (Remote, error) {
var servers = make([]*server, len(remotes))
for i := range remotes {
u, err := normalizeURL(remotes[i])
if err != nil {
return nil, err
}
servers[i] = newServer(u, tlsConfig)
}
switch strategy {
case StrategyOrderedList:
return newOrdererdListGroup(servers)
default:
return nil, errors.New("unrecognised strategy")
}
}
type orderedListGroup struct {
remotes []*server
}
func (g *orderedListGroup) Hosts() []string {
var hosts = make([]string, 0, len(g.remotes))
for _, srv := range g.remotes {
srvHosts := srv.Hosts()
hosts = append(hosts, srvHosts[0])
}
return hosts
}
func (g *orderedListGroup) SetRequestTimeout(timeout time.Duration) {
for _, srv := range g.remotes {
srv.SetRequestTimeout(timeout)
}
}
func (g *orderedListGroup) SetProxy(proxy func(*http.Request) (*url.URL, error)) {
for _, srv := range g.remotes {
srv.SetProxy(proxy)
}
}
func newOrdererdListGroup(remotes []*server) (Remote, error) {
return &orderedListGroup{
remotes: remotes,
}, nil
}
func (g *orderedListGroup) AuthSign(req, id []byte, provider auth.Provider) (resp []byte, err error) {
for i := range g.remotes {
resp, err = g.remotes[i].AuthSign(req, id, provider)
if err == nil {
return resp, nil
}
}
return nil, err
}
func (g *orderedListGroup) Sign(jsonData []byte) (resp []byte, err error) {
for i := range g.remotes {
resp, err = g.remotes[i].Sign(jsonData)
if err == nil {
return resp, nil
}
}
return nil, err
}
func (g *orderedListGroup) Info(jsonData []byte) (resp *info.Resp, err error) {
for i := range g.remotes {
resp, err = g.remotes[i].Info(jsonData)
if err == nil {
return resp, nil
}
}
return nil, err
}
// SetReqModifier does nothing because there is no request modifier for group
func (g *orderedListGroup) SetReqModifier(mod func(*http.Request, []byte)) {
// noop
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["crl.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/crl",
importpath = "github.com/cloudflare/cfssl/api/crl",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/certdb:go_default_library",
"//vendor/github.com/cloudflare/cfssl/crl:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package crl implements the HTTP handler for the crl command.
package crl
import (
"crypto"
"crypto/x509"
"net/http"
"os"
"time"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/certdb"
"github.com/cloudflare/cfssl/crl"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
)
// A Handler accepts requests with a serial number parameter
// and revokes
type Handler struct {
dbAccessor certdb.Accessor
ca *x509.Certificate
key crypto.Signer
}
// NewHandler returns a new http.Handler that handles a revoke request.
func NewHandler(dbAccessor certdb.Accessor, caPath string, caKeyPath string) (http.Handler, error) {
ca, err := helpers.ReadBytes(caPath)
if err != nil {
return nil, err
}
caKey, err := helpers.ReadBytes(caKeyPath)
if err != nil {
return nil, errors.Wrap(errors.PrivateKeyError, errors.ReadFailed, err)
}
// Parse the PEM encoded certificate
issuerCert, err := helpers.ParseCertificatePEM(ca)
if err != nil {
return nil, err
}
strPassword := os.Getenv("CFSSL_CA_PK_PASSWORD")
password := []byte(strPassword)
if strPassword == "" {
password = nil
}
// Parse the key given
key, err := helpers.ParsePrivateKeyPEMWithPassword(caKey, password)
if err != nil {
log.Debug("malformed private key %v", err)
return nil, err
}
return &api.HTTPHandler{
Handler: &Handler{
dbAccessor: dbAccessor,
ca: issuerCert,
key: key,
},
Methods: []string{"GET"},
}, nil
}
// Handle responds to revocation requests. It attempts to revoke
// a certificate with a given serial number
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
var newExpiryTime = 7 * helpers.OneDay
certs, err := h.dbAccessor.GetRevokedAndUnexpiredCertificates()
if err != nil {
return err
}
queryExpiryTime := r.URL.Query().Get("expiry")
if queryExpiryTime != "" {
log.Infof("requested expiry time of %s", queryExpiryTime)
newExpiryTime, err = time.ParseDuration(queryExpiryTime)
if err != nil {
return err
}
}
result, err := crl.NewCRLFromDB(certs, h.ca, h.key, newExpiryTime)
if err != nil {
return err
}
return api.SendResponse(w, result)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["gencrl.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/gencrl",
importpath = "github.com/cloudflare/cfssl/api/gencrl",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package gencrl implements the HTTP handler for the gencrl commands.
package gencrl
import (
"crypto/rand"
"crypto/x509/pkix"
"encoding/json"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
"io/ioutil"
"math/big"
"net/http"
"strconv"
"strings"
"time"
)
// This type is meant to be unmarshalled from JSON
type jsonCRLRequest struct {
Certificate string `json:"certificate"`
SerialNumber []string `json:"serialNumber"`
PrivateKey string `json:"issuingKey"`
ExpiryTime string `json:"expireTime"`
}
// Handle responds to requests for crl generation. It creates this crl
// based off of the given certificate, serial numbers, and private key
func gencrlHandler(w http.ResponseWriter, r *http.Request) error {
var revokedCerts []pkix.RevokedCertificate
var oneWeek = time.Duration(604800) * time.Second
var newExpiryTime = time.Now()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
r.Body.Close()
req := &jsonCRLRequest{}
err = json.Unmarshal(body, req)
if err != nil {
log.Error(err)
}
if req.ExpiryTime != "" {
expiryTime := strings.TrimSpace(req.ExpiryTime)
expiryInt, err := strconv.ParseInt(expiryTime, 0, 32)
if err != nil {
return err
}
newExpiryTime = time.Now().Add((time.Duration(expiryInt) * time.Second))
}
if req.ExpiryTime == "" {
newExpiryTime = time.Now().Add(oneWeek)
}
if err != nil {
return err
}
cert, err := helpers.ParseCertificatePEM([]byte(req.Certificate))
if err != nil {
log.Error("error from ParseCertificatePEM", err)
return errors.NewBadRequestString("malformed certificate")
}
for _, value := range req.SerialNumber {
tempBigInt := new(big.Int)
tempBigInt.SetString(value, 10)
tempCert := pkix.RevokedCertificate{
SerialNumber: tempBigInt,
RevocationTime: time.Now(),
}
revokedCerts = append(revokedCerts, tempCert)
}
key, err := helpers.ParsePrivateKeyPEM([]byte(req.PrivateKey))
if err != nil {
log.Debug("malformed private key %v", err)
return errors.NewBadRequestString("malformed Private Key")
}
result, err := cert.CreateCRL(rand.Reader, key, revokedCerts, time.Now(), newExpiryTime)
if err != nil {
log.Debug("unable to create CRL: %v", err)
return err
}
return api.SendResponse(w, result)
}
// NewHandler returns a new http.Handler that handles a crl generation request.
func NewHandler() http.Handler {
return api.HTTPHandler{
Handler: api.HandlerFunc(gencrlHandler),
Methods: []string{"POST"},
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["generator.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/generator",
importpath = "github.com/cloudflare/cfssl/api/generator",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/bundler:go_default_library",
"//vendor/github.com/cloudflare/cfssl/config:go_default_library",
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/signer:go_default_library",
"//vendor/github.com/cloudflare/cfssl/signer/universal:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package generator implements the HTTP handlers for certificate generation.
package generator
import (
"crypto/md5"
"crypto/sha1"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/bundler"
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
"github.com/cloudflare/cfssl/signer/universal"
)
const (
// CSRNoHostMessage is used to alert the user to a certificate lacking a hosts field.
CSRNoHostMessage = `This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").`
// NoBundlerMessage is used to alert the user that the server does not have a bundler initialized.
NoBundlerMessage = `This request requires a bundler, but one is not initialized for the API server.`
)
// Sum contains digests for a certificate or certificate request.
type Sum struct {
MD5 string `json:"md5"`
SHA1 string `json:"sha-1"`
}
// Validator is a type of function that contains the logic for validating
// a certificate request.
type Validator func(*csr.CertificateRequest) error
// A CertRequest stores a PEM-encoded private key and corresponding
// CSR; this is returned from the CSR generation endpoint.
type CertRequest struct {
Key string `json:"private_key"`
CSR string `json:"certificate_request"`
Sums map[string]Sum `json:"sums"`
}
// A Handler accepts JSON-encoded certificate requests and
// returns a new private key and certificate request.
type Handler struct {
generator *csr.Generator
}
// NewHandler builds a new Handler from the
// validation function provided.
func NewHandler(validator Validator) (http.Handler, error) {
log.Info("setting up key / CSR generator")
return &api.HTTPHandler{
Handler: &Handler{
generator: &csr.Generator{Validator: validator},
},
Methods: []string{"POST"},
}, nil
}
func computeSum(in []byte) (sum Sum, err error) {
var data []byte
p, _ := pem.Decode(in)
if p == nil {
err = errors.NewBadRequestString("not a CSR or certificate")
return
}
switch p.Type {
case "CERTIFICATE REQUEST":
var req *x509.CertificateRequest
req, err = x509.ParseCertificateRequest(p.Bytes)
if err != nil {
return
}
data = req.Raw
case "CERTIFICATE":
var cert *x509.Certificate
cert, err = x509.ParseCertificate(p.Bytes)
if err != nil {
return
}
data = cert.Raw
default:
err = errors.NewBadRequestString("not a CSR or certificate")
return
}
md5Sum := md5.Sum(data)
sha1Sum := sha1.Sum(data)
sum.MD5 = fmt.Sprintf("%X", md5Sum[:])
sum.SHA1 = fmt.Sprintf("%X", sha1Sum[:])
return
}
// Handle responds to requests for the CA to generate a new private
// key and certificate request on behalf of the client. The format for
// these requests is documented in the API documentation.
func (g *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("request for CSR")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
req := new(csr.CertificateRequest)
req.KeyRequest = csr.NewBasicKeyRequest()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
if req.CA != nil {
log.Warningf("request received with CA section")
return errors.NewBadRequestString("ca section only permitted in initca")
}
csr, key, err := g.generator.ProcessRequest(req)
if err != nil {
log.Warningf("failed to process CSR: %v", err)
// The validator returns a *cfssl/errors.HttpError
return err
}
sum, err := computeSum(csr)
if err != nil {
return errors.NewBadRequest(err)
}
// Both key and csr are returned PEM-encoded.
response := api.NewSuccessResponse(&CertRequest{
Key: string(key),
CSR: string(csr),
Sums: map[string]Sum{"certificate_request": sum},
})
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
err = enc.Encode(response)
return err
}
// A CertGeneratorHandler accepts JSON-encoded certificate requests
// and returns a new private key and signed certificate; it handles
// sending the CSR to the server.
type CertGeneratorHandler struct {
generator *csr.Generator
bundler *bundler.Bundler
signer signer.Signer
}
// NewCertGeneratorHandler builds a new handler for generating
// certificates directly from certificate requests; the validator covers
// the certificate request and the CA's key and certificate are used to
// sign the generated request. If remote is not an empty string, the
// handler will send signature requests to the CFSSL instance contained
// in remote.
func NewCertGeneratorHandler(validator Validator, caFile, caKeyFile string, policy *config.Signing) (http.Handler, error) {
var err error
log.Info("setting up new generator / signer")
cg := new(CertGeneratorHandler)
if policy == nil {
policy = &config.Signing{
Default: config.DefaultConfig(),
Profiles: nil,
}
}
root := universal.Root{
Config: map[string]string{
"ca-file": caFile,
"ca-key-file": caKeyFile,
},
}
if cg.signer, err = universal.NewSigner(root, policy); err != nil {
log.Errorf("setting up signer failed: %v", err)
return nil, err
}
cg.generator = &csr.Generator{Validator: validator}
return api.HTTPHandler{Handler: cg, Methods: []string{"POST"}}, nil
}
// NewCertGeneratorHandlerFromSigner returns a handler directly from
// the signer and validation function.
func NewCertGeneratorHandlerFromSigner(validator Validator, signer signer.Signer) http.Handler {
return api.HTTPHandler{
Handler: &CertGeneratorHandler{
generator: &csr.Generator{Validator: validator},
signer: signer,
},
Methods: []string{"POST"},
}
}
// SetBundler allows injecting an optional Bundler into the CertGeneratorHandler.
func (cg *CertGeneratorHandler) SetBundler(caBundleFile, intBundleFile string) (err error) {
cg.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
return err
}
type genSignRequest struct {
Request *csr.CertificateRequest `json:"request"`
Profile string `json:"profile"`
Label string `json:"label"`
Bundle bool `json:"bundle"`
}
// Handle responds to requests for the CA to generate a new private
// key and certificate on behalf of the client. The format for these
// requests is documented in the API documentation.
func (cg *CertGeneratorHandler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("request for CSR")
req := new(genSignRequest)
req.Request = csr.New()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
if req.Request == nil {
log.Warning("empty request received")
return errors.NewBadRequestString("missing request section")
}
if req.Request.CA != nil {
log.Warningf("request received with CA section")
return errors.NewBadRequestString("ca section only permitted in initca")
}
csr, key, err := cg.generator.ProcessRequest(req.Request)
if err != nil {
log.Warningf("failed to process CSR: %v", err)
// The validator returns a *cfssl/errors.HttpError
return err
}
signReq := signer.SignRequest{
Request: string(csr),
Profile: req.Profile,
Label: req.Label,
}
certBytes, err := cg.signer.Sign(signReq)
if err != nil {
log.Warningf("failed to sign request: %v", err)
return err
}
reqSum, err := computeSum(csr)
if err != nil {
return errors.NewBadRequest(err)
}
certSum, err := computeSum(certBytes)
if err != nil {
return errors.NewBadRequest(err)
}
result := map[string]interface{}{
"private_key": string(key),
"certificate_request": string(csr),
"certificate": string(certBytes),
"sums": map[string]Sum{
"certificate_request": reqSum,
"certificate": certSum,
},
}
if req.Bundle {
if cg.bundler == nil {
return api.SendResponseWithMessage(w, result, NoBundlerMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
bundle, err := cg.bundler.BundleFromPEMorDER(certBytes, nil, bundler.Optimal, "")
if err != nil {
return err
}
result["bundle"] = bundle
}
if len(req.Request.Hosts) == 0 {
return api.SendResponseWithMessage(w, result, CSRNoHostMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
return api.SendResponse(w, result)
}
// CSRValidate does nothing and will never return an error. It exists because NewHandler
// requires a Validator as a parameter.
func CSRValidate(req *csr.CertificateRequest) error {
return nil
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["health.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/health",
importpath = "github.com/cloudflare/cfssl/api/health",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/cloudflare/cfssl/api:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package health
import (
"encoding/json"
"net/http"
"github.com/cloudflare/cfssl/api"
)
// Response contains the response to the /health API
type Response struct {
Healthy bool `json:"healthy"`
}
func healthHandler(w http.ResponseWriter, r *http.Request) error {
response := api.NewSuccessResponse(&Response{Healthy: true})
return json.NewEncoder(w).Encode(response)
}
// NewHealthCheck creates a new handler to serve health checks.
func NewHealthCheck() http.Handler {
return api.HTTPHandler{
Handler: api.HandlerFunc(healthHandler),
Methods: []string{"GET"},
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["info.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/info",
importpath = "github.com/cloudflare/cfssl/api/info",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/info:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/signer:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package info implements the HTTP handler for the info command.
package info
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/info"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
)
// Handler is a type that contains the root certificates for the CA,
// and serves information on them for clients that need the certificates.
type Handler struct {
sign signer.Signer
}
// NewHandler creates a new handler to serve information on the CA's
// certificates, taking a signer to use.
func NewHandler(s signer.Signer) (http.Handler, error) {
return &api.HTTPHandler{
Handler: &Handler{
sign: s,
},
Methods: []string{"POST"},
}, nil
}
// Handle listens for incoming requests for CA information, and returns
// a list containing information on each root certificate.
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
req := new(info.Req)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
resp, err := h.sign.Info(*req)
if err != nil {
return err
}
response := api.NewSuccessResponse(resp)
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
return enc.Encode(response)
}
// MultiHandler is a handler for providing the public certificates for
// a multi-root certificate authority. It takes a mapping of label to
// signer and a default label, and handles the standard information
// request as defined in the client package.
type MultiHandler struct {
signers map[string]signer.Signer
defaultLabel string
}
// NewMultiHandler constructs a MultiHandler from a mapping of labels
// to signers and the default label.
func NewMultiHandler(signers map[string]signer.Signer, defaultLabel string) (http.Handler, error) {
return &api.HTTPHandler{
Handler: &MultiHandler{
signers: signers,
defaultLabel: defaultLabel,
},
Methods: []string{"POST"},
}, nil
}
// Handle accepts client information requests, and uses the label to
// look up the signer whose public certificate should be retrieved. If
// the label is empty, the default label is used.
func (h *MultiHandler) Handle(w http.ResponseWriter, r *http.Request) error {
req := new(info.Req)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
log.Debug("checking label")
if req.Label == "" {
req.Label = h.defaultLabel
}
if _, ok := h.signers[req.Label]; !ok {
log.Warningf("request for invalid endpoint")
return errors.NewBadRequestString("bad label")
}
log.Debug("getting info")
resp, err := h.signers[req.Label].Info(*req)
if err != nil {
log.Infof("error getting certificate: %v", err)
return err
}
response := api.NewSuccessResponse(resp)
w.Header().Set("Content-Type", "application/json")
enc := json.NewEncoder(w)
return enc.Encode(response)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["initca.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/initca",
importpath = "github.com/cloudflare/cfssl/api/initca",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/initca:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package initca implements the HTTP handler for the CA initialization command
package initca
import (
"encoding/json"
"io/ioutil"
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/csr"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/initca"
"github.com/cloudflare/cfssl/log"
)
// A NewCA contains a private key and certificate suitable for serving
// as the root key for a new certificate authority.
type NewCA struct {
Key string `json:"private_key"`
Cert string `json:"certificate"`
}
// initialCAHandler is an HTTP handler that accepts a JSON blob in the
// same format as the CSR endpoint; this blob should contain the
// identity information for the CA's root key. This endpoint is not
// suitable for creating intermediate certificates.
func initialCAHandler(w http.ResponseWriter, r *http.Request) error {
log.Info("setting up initial CA handler")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Warningf("failed to read request body: %v", err)
return errors.NewBadRequest(err)
}
r.Body.Close()
req := new(csr.CertificateRequest)
req.KeyRequest = csr.NewBasicKeyRequest()
err = json.Unmarshal(body, req)
if err != nil {
log.Warningf("failed to unmarshal request: %v", err)
return errors.NewBadRequest(err)
}
cert, _, key, err := initca.New(req)
if err != nil {
log.Warningf("failed to initialise new CA: %v", err)
return err
}
response := api.NewSuccessResponse(&NewCA{string(key), string(cert)})
enc := json.NewEncoder(w)
err = enc.Encode(response)
return err
}
// NewHandler returns a new http.Handler that handles request to
// initialize a CA.
func NewHandler() http.Handler {
return api.HTTPHandler{Handler: api.HandlerFunc(initialCAHandler), Methods: []string{"POST"}}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["ocspsign.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/ocsp",
importpath = "github.com/cloudflare/cfssl/api/ocsp",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/ocsp:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package ocsp implements the HTTP handler for the ocsp commands.
package ocsp
import (
"crypto"
"net/http"
"encoding/base64"
"encoding/json"
"io/ioutil"
"time"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/ocsp"
)
// A Handler accepts requests with a certficate parameter
// (which should be PEM-encoded) and returns a signed ocsp
// response.
type Handler struct {
signer ocsp.Signer
}
// NewHandler returns a new http.Handler that handles a ocspsign request.
func NewHandler(s ocsp.Signer) http.Handler {
return &api.HTTPHandler{
Handler: &Handler{
signer: s,
},
Methods: []string{"POST"},
}
}
// This type is meant to be unmarshalled from JSON
type jsonSignRequest struct {
Certificate string `json:"certificate"`
Status string `json:"status"`
Reason int `json:"reason,omitempty"`
RevokedAt string `json:"revoked_at,omitempty"`
IssuerHash string `json:"issuer_hash,omitempty"`
}
var nameToHash = map[string]crypto.Hash{
"MD5": crypto.MD5,
"SHA1": crypto.SHA1,
"SHA256": crypto.SHA256,
"SHA384": crypto.SHA384,
"SHA512": crypto.SHA512,
}
// Handle responds to requests for a ocsp signature. It creates and signs
// a ocsp response for the provided certificate and status. If the status
// is revoked then it also adds reason and revoked_at. The response is
// base64 encoded.
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
r.Body.Close()
// Default the status to good so it matches the cli
req := &jsonSignRequest{
Status: "good",
}
err = json.Unmarshal(body, req)
if err != nil {
return errors.NewBadRequestString("Unable to parse sign request")
}
cert, err := helpers.ParseCertificatePEM([]byte(req.Certificate))
if err != nil {
log.Error("Error from ParseCertificatePEM", err)
return errors.NewBadRequestString("Malformed certificate")
}
signReq := ocsp.SignRequest{
Certificate: cert,
Status: req.Status,
}
// We need to convert the time from being a string to a time.Time
if req.Status == "revoked" {
signReq.Reason = req.Reason
// "now" is accepted and the default on the cli so default that here
if req.RevokedAt == "" || req.RevokedAt == "now" {
signReq.RevokedAt = time.Now()
} else {
signReq.RevokedAt, err = time.Parse("2006-01-02", req.RevokedAt)
if err != nil {
return errors.NewBadRequestString("Malformed revocation time")
}
}
}
if req.IssuerHash != "" {
issuerHash, ok := nameToHash[req.IssuerHash]
if !ok {
return errors.NewBadRequestString("Unsupported hash algorithm in request")
}
signReq.IssuerHash = issuerHash
}
resp, err := h.signer.Sign(signReq)
if err != nil {
return err
}
b64Resp := base64.StdEncoding.EncodeToString(resp)
result := map[string]string{"ocspResponse": b64Resp}
return api.SendResponse(w, result)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["revoke.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/revoke",
importpath = "github.com/cloudflare/cfssl/api/revoke",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/certdb:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/ocsp:go_default_library",
"//vendor/golang.org/x/crypto/ocsp:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package revoke implements the HTTP handler for the revoke command
package revoke
import (
"encoding/json"
"io/ioutil"
"net/http"
"time"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/certdb"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/ocsp"
stdocsp "golang.org/x/crypto/ocsp"
)
// A Handler accepts requests with a serial number parameter
// and revokes
type Handler struct {
dbAccessor certdb.Accessor
Signer ocsp.Signer
}
// NewHandler returns a new http.Handler that handles a revoke request.
func NewHandler(dbAccessor certdb.Accessor) http.Handler {
return &api.HTTPHandler{
Handler: &Handler{
dbAccessor: dbAccessor,
},
Methods: []string{"POST"},
}
}
// NewOCSPHandler returns a new http.Handler that handles a revoke
// request and also generates an OCSP response
func NewOCSPHandler(dbAccessor certdb.Accessor, signer ocsp.Signer) http.Handler {
return &api.HTTPHandler{
Handler: &Handler{
dbAccessor: dbAccessor,
Signer: signer,
},
Methods: []string{"POST"},
}
}
// This type is meant to be unmarshalled from JSON
type jsonRevokeRequest struct {
Serial string `json:"serial"`
AKI string `json:"authority_key_id"`
Reason string `json:"reason"`
}
// Handle responds to revocation requests. It attempts to revoke
// a certificate with a given serial number
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
r.Body.Close()
// Default the status to good so it matches the cli
var req jsonRevokeRequest
err = json.Unmarshal(body, &req)
if err != nil {
return errors.NewBadRequestString("Unable to parse revocation request")
}
if len(req.Serial) == 0 {
return errors.NewBadRequestString("serial number is required but not provided")
}
var reasonCode int
reasonCode, err = ocsp.ReasonStringToCode(req.Reason)
if err != nil {
return errors.NewBadRequestString("Invalid reason code")
}
err = h.dbAccessor.RevokeCertificate(req.Serial, req.AKI, reasonCode)
if err != nil {
return err
}
// If we were given a signer, try and generate an OCSP
// response indicating revocation
if h.Signer != nil {
// TODO: should these errors be errors?
// Grab the certificate from the database
cr, err := h.dbAccessor.GetCertificate(req.Serial, req.AKI)
if err != nil {
return err
}
if len(cr) != 1 {
return errors.NewBadRequestString("No unique certificate found")
}
cert, err := helpers.ParseCertificatePEM([]byte(cr[0].PEM))
if err != nil {
return errors.NewBadRequestString("Unable to parse certificates from PEM data")
}
sr := ocsp.SignRequest{
Certificate: cert,
Status: "revoked",
Reason: reasonCode,
RevokedAt: time.Now().UTC(),
}
ocspResponse, err := h.Signer.Sign(sr)
if err != nil {
return err
}
// We parse the OCSP response in order to get the next
// update time/expiry time
ocspParsed, err := stdocsp.ParseResponse(ocspResponse, nil)
if err != nil {
return err
}
ocspRecord := certdb.OCSPRecord{
Serial: req.Serial,
AKI: req.AKI,
Body: string(ocspResponse),
Expiry: ocspParsed.NextUpdate,
}
if err = h.dbAccessor.InsertOCSP(ocspRecord); err != nil {
return err
}
}
result := map[string]string{}
return api.SendResponse(w, result)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["scan.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/scan",
importpath = "github.com/cloudflare/cfssl/api/scan",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/scan:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package scan
import (
"encoding/json"
"net/http"
"time"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/scan"
)
// scanHandler is an HTTP handler that accepts GET parameters for host (required)
// family and scanner, and uses these to perform scans, returning a JSON blob result.
func scanHandler(w http.ResponseWriter, r *http.Request) error {
if err := r.ParseForm(); err != nil {
log.Warningf("failed to parse body: %v", err)
return errors.NewBadRequest(err)
}
family := r.Form.Get("family")
scanner := r.Form.Get("scanner")
ip := r.Form.Get("ip")
timeoutStr := r.Form.Get("timeout")
var timeout time.Duration
var err error
if timeoutStr != "" {
if timeout, err = time.ParseDuration(timeoutStr); err != nil {
return errors.NewBadRequest(err)
}
if timeout < time.Second || timeout > 5*time.Minute {
return errors.NewBadRequestString("invalid timeout given")
}
} else {
timeout = time.Minute
}
host := r.Form.Get("host")
if host == "" {
log.Warningf("no host given")
return errors.NewBadRequestString("no host given")
}
results, err := scan.Default.RunScans(host, ip, family, scanner, timeout)
if err != nil {
return errors.NewBadRequest(err)
}
return json.NewEncoder(w).Encode(api.NewSuccessResponse(results))
}
// NewHandler returns a new http.Handler that handles a scan request.
func NewHandler(caBundleFile string) (http.Handler, error) {
return api.HTTPHandler{
Handler: api.HandlerFunc(scanHandler),
Methods: []string{"GET"},
}, scan.LoadRootCAs(caBundleFile)
}
// scanInfoHandler is an HTTP handler that returns a JSON blob result describing
// the possible families and scans to be run.
func scanInfoHandler(w http.ResponseWriter, r *http.Request) error {
log.Info("setting up scaninfo handler")
response := api.NewSuccessResponse(scan.Default)
enc := json.NewEncoder(w)
return enc.Encode(response)
}
// NewInfoHandler returns a new http.Handler that handles a request for scan info.
func NewInfoHandler() http.Handler {
return api.HTTPHandler{
Handler: api.HandlerFunc(scanInfoHandler),
Methods: []string{"GET"},
}
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["signhandler.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/api/signhandler",
importpath = "github.com/cloudflare/cfssl/api/signhandler",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/api:go_default_library",
"//vendor/github.com/cloudflare/cfssl/auth:go_default_library",
"//vendor/github.com/cloudflare/cfssl/bundler:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/signer:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
// Package signhandler provides the handlers for signers.
package signhandler
import (
"encoding/json"
"io/ioutil"
"math/big"
"net/http"
"github.com/cloudflare/cfssl/api"
"github.com/cloudflare/cfssl/auth"
"github.com/cloudflare/cfssl/bundler"
"github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
)
// NoBundlerMessage is used to alert the user that the server does not have a bundler initialized.
const NoBundlerMessage = `This request requires a bundler, but one is not initialized for the API server.`
// A Handler accepts requests with a hostname and certficate
// parameter (which should be PEM-encoded) and returns a new signed
// certificate. It includes upstream servers indexed by their
// profile name.
type Handler struct {
signer signer.Signer
bundler *bundler.Bundler
}
// NewHandlerFromSigner generates a new Handler directly from
// an existing signer.
func NewHandlerFromSigner(signer signer.Signer) (h *api.HTTPHandler, err error) {
policy := signer.Policy()
if policy == nil {
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
return
}
// Sign will only respond for profiles that have no auth provider.
// So if all of the profiles require authentication, we return an error.
haveUnauth := (policy.Default.Provider == nil)
for _, profile := range policy.Profiles {
haveUnauth = haveUnauth || (profile.Provider == nil)
}
if !haveUnauth {
err = errors.New(errors.PolicyError, errors.InvalidPolicy)
return
}
return &api.HTTPHandler{
Handler: &Handler{
signer: signer,
},
Methods: []string{"POST"},
}, nil
}
// SetBundler allows injecting an optional Bundler into the Handler.
func (h *Handler) SetBundler(caBundleFile, intBundleFile string) (err error) {
h.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
return err
}
// This type is meant to be unmarshalled from JSON so that there can be a
// hostname field in the API
// TODO: Change the API such that the normal struct can be used.
type jsonSignRequest struct {
Hostname string `json:"hostname"`
Hosts []string `json:"hosts"`
Request string `json:"certificate_request"`
Subject *signer.Subject `json:"subject,omitempty"`
Profile string `json:"profile"`
Label string `json:"label"`
Serial *big.Int `json:"serial,omitempty"`
Bundle bool `json:"bundle"`
}
func jsonReqToTrue(js jsonSignRequest) signer.SignRequest {
sub := new(signer.Subject)
if js.Subject == nil {
sub = nil
} else {
// make a copy
*sub = *js.Subject
}
if js.Hostname != "" {
return signer.SignRequest{
Hosts: signer.SplitHosts(js.Hostname),
Subject: sub,
Request: js.Request,
Profile: js.Profile,
Label: js.Label,
Serial: js.Serial,
}
}
return signer.SignRequest{
Hosts: js.Hosts,
Subject: sub,
Request: js.Request,
Profile: js.Profile,
Label: js.Label,
Serial: js.Serial,
}
}
// Handle responds to requests for the CA to sign the certificate request
// present in the "certificate_request" parameter for the host named
// in the "hostname" parameter. The certificate should be PEM-encoded. If
// provided, subject information from the "subject" parameter will be used
// in place of the subject information from the CSR.
func (h *Handler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("signature request received")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
r.Body.Close()
var req jsonSignRequest
err = json.Unmarshal(body, &req)
if err != nil {
return errors.NewBadRequestString("Unable to parse sign request")
}
signReq := jsonReqToTrue(req)
if req.Request == "" {
return errors.NewBadRequestString("missing parameter 'certificate_request'")
}
var cert []byte
profile, err := signer.Profile(h.signer, req.Profile)
if err != nil {
return err
}
if profile.Provider != nil {
log.Error("profile requires authentication")
return errors.NewBadRequestString("authentication required")
}
cert, err = h.signer.Sign(signReq)
if err != nil {
log.Warningf("failed to sign request: %v", err)
return err
}
result := map[string]interface{}{"certificate": string(cert)}
if req.Bundle {
if h.bundler == nil {
return api.SendResponseWithMessage(w, result, NoBundlerMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
bundle, err := h.bundler.BundleFromPEMorDER(cert, nil, bundler.Optimal, "")
if err != nil {
return err
}
result["bundle"] = bundle
}
log.Info("wrote response")
return api.SendResponse(w, result)
}
// An AuthHandler verifies and signs incoming signature requests.
type AuthHandler struct {
signer signer.Signer
bundler *bundler.Bundler
}
// NewAuthHandlerFromSigner creates a new AuthHandler from the signer
// that is passed in.
func NewAuthHandlerFromSigner(signer signer.Signer) (http.Handler, error) {
policy := signer.Policy()
if policy == nil {
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
}
if policy.Default == nil && policy.Profiles == nil {
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
}
// AuthSign will not respond for profiles that have no auth provider.
// So if there are no profiles with auth providers in this policy,
// we return an error.
haveAuth := (policy.Default.Provider != nil)
for _, profile := range policy.Profiles {
if haveAuth {
break
}
haveAuth = (profile.Provider != nil)
}
if !haveAuth {
return nil, errors.New(errors.PolicyError, errors.InvalidPolicy)
}
return &api.HTTPHandler{
Handler: &AuthHandler{
signer: signer,
},
Methods: []string{"POST"},
}, nil
}
// SetBundler allows injecting an optional Bundler into the Handler.
func (h *AuthHandler) SetBundler(caBundleFile, intBundleFile string) (err error) {
h.bundler, err = bundler.NewBundler(caBundleFile, intBundleFile)
return err
}
// Handle receives the incoming request, validates it, and processes it.
func (h *AuthHandler) Handle(w http.ResponseWriter, r *http.Request) error {
log.Info("signature request received")
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Errorf("failed to read response body: %v", err)
return err
}
r.Body.Close()
var aReq auth.AuthenticatedRequest
err = json.Unmarshal(body, &aReq)
if err != nil {
log.Errorf("failed to unmarshal authenticated request: %v", err)
return errors.NewBadRequest(err)
}
var req jsonSignRequest
err = json.Unmarshal(aReq.Request, &req)
if err != nil {
log.Errorf("failed to unmarshal request from authenticated request: %v", err)
return errors.NewBadRequestString("Unable to parse authenticated sign request")
}
// Sanity checks to ensure that we have a valid policy. This
// should have been checked in NewAuthHandler.
policy := h.signer.Policy()
if policy == nil {
log.Critical("signer was initialised without a signing policy")
return errors.NewBadRequestString("invalid policy")
}
profile, err := signer.Profile(h.signer, req.Profile)
if err != nil {
return err
}
if profile.Provider == nil {
log.Error("profile has no authentication provider")
return errors.NewBadRequestString("no authentication provider")
}
if !profile.Provider.Verify(&aReq) {
log.Warning("received authenticated request with invalid token")
return errors.NewBadRequestString("invalid token")
}
signReq := jsonReqToTrue(req)
if signReq.Request == "" {
return errors.NewBadRequestString("missing parameter 'certificate_request'")
}
cert, err := h.signer.Sign(signReq)
if err != nil {
log.Errorf("signature failed: %v", err)
return err
}
result := map[string]interface{}{"certificate": string(cert)}
if req.Bundle {
if h.bundler == nil {
return api.SendResponseWithMessage(w, result, NoBundlerMessage,
errors.New(errors.PolicyError, errors.InvalidRequest).ErrorCode)
}
bundle, err := h.bundler.BundleFromPEMorDER(cert, nil, bundler.Optimal, "")
if err != nil {
return err
}
result["bundle"] = bundle
}
log.Info("wrote response")
return api.SendResponse(w, result)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"bundle.go",
"bundler.go",
"doc.go",
],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/bundler",
importpath = "github.com/cloudflare/cfssl/bundler",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/ubiquity:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package bundler
import (
"bytes"
"crypto/ecdsa"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"time"
"github.com/cloudflare/cfssl/helpers"
)
// A Bundle contains a certificate and its trust chain. It is intended
// to store the most widely applicable chain, with shortness an
// explicit goal.
type Bundle struct {
Chain []*x509.Certificate
Cert *x509.Certificate
Root *x509.Certificate
Key interface{}
Issuer *pkix.Name
Subject *pkix.Name
Expires time.Time
LeafExpires time.Time
Hostnames []string
Status *BundleStatus
}
// BundleStatus is designated for various status reporting.
type BundleStatus struct {
// A flag on whether a new bundle is generated
IsRebundled bool `json:"rebundled"`
// A list of SKIs of expiring certificates
ExpiringSKIs []string `json:"expiring_SKIs"`
// A list of untrusted root store names
Untrusted []string `json:"untrusted_root_stores"`
// A list of human readable warning messages based on the bundle status.
Messages []string `json:"messages"`
// A status code consists of binary flags
Code int `json:"code"`
}
type chain []*x509.Certificate
func (c chain) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
for _, cert := range c {
buf.Write(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}))
}
ret := bytes.TrimSpace(buf.Bytes())
return json.Marshal(string(ret))
}
// PemBlockToString turns a pem.Block into the string encoded form.
func PemBlockToString(block *pem.Block) string {
if block.Bytes == nil || block.Type == "" {
return ""
}
return string(bytes.TrimSpace(pem.EncodeToMemory(block)))
}
var typeToName = map[int]string{
3: "CommonName",
5: "SerialNumber",
6: "Country",
7: "Locality",
8: "Province",
9: "StreetAddress",
10: "Organization",
11: "OrganizationalUnit",
17: "PostalCode",
}
type names []pkix.AttributeTypeAndValue
func (n names) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
for _, name := range n {
buf.WriteString(fmt.Sprintf("/%s=%s", typeToName[name.Type[3]], name.Value))
}
return json.Marshal(buf.String())
}
// MarshalJSON serialises the bundle to JSON. The resulting JSON
// structure contains the bundle (as a sequence of PEM-encoded
// certificates), the certificate, the private key, the size of they
// key, the issuer(s), the subject name(s), the expiration, the
// hostname(s), the OCSP server, and the signature on the certificate.
func (b *Bundle) MarshalJSON() ([]byte, error) {
if b == nil || b.Cert == nil {
return nil, errors.New("no certificate in bundle")
}
var keyBytes, rootBytes []byte
var keyLength int
var keyType, keyString string
keyLength = helpers.KeyLength(b.Cert.PublicKey)
switch b.Cert.PublicKeyAlgorithm {
case x509.ECDSA:
keyType = fmt.Sprintf("%d-bit ECDSA", keyLength)
case x509.RSA:
keyType = fmt.Sprintf("%d-bit RSA", keyLength)
case x509.DSA:
keyType = "DSA"
default:
keyType = "Unknown"
}
switch key := b.Key.(type) {
case *rsa.PrivateKey:
keyBytes = x509.MarshalPKCS1PrivateKey(key)
keyString = PemBlockToString(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes})
case *ecdsa.PrivateKey:
keyBytes, _ = x509.MarshalECPrivateKey(key)
keyString = PemBlockToString(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})
case fmt.Stringer:
keyString = key.String()
}
if len(b.Hostnames) == 0 {
b.buildHostnames()
}
var ocspSupport = false
if b.Cert.OCSPServer != nil {
ocspSupport = true
}
var crlSupport = false
if b.Cert.CRLDistributionPoints != nil {
crlSupport = true
}
if b.Root != nil {
rootBytes = b.Root.Raw
}
return json.Marshal(map[string]interface{}{
"bundle": chain(b.Chain),
"root": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: rootBytes}),
"crt": PemBlockToString(&pem.Block{Type: "CERTIFICATE", Bytes: b.Cert.Raw}),
"key": keyString,
"key_type": keyType,
"key_size": keyLength,
"issuer": names(b.Issuer.Names),
"subject": names(b.Subject.Names),
"expires": b.Expires,
"leaf_expires": b.LeafExpires,
"hostnames": b.Hostnames,
"ocsp_support": ocspSupport,
"crl_support": crlSupport,
"ocsp": b.Cert.OCSPServer,
"signature": helpers.SignatureString(b.Cert.SignatureAlgorithm),
"status": b.Status,
})
}
// buildHostnames sets bundle.Hostnames by the x509 cert's subject CN and DNS names
// Since the subject CN may overlap with one of the DNS names, it needs to handle
// the duplication by a set.
func (b *Bundle) buildHostnames() {
if b.Cert == nil {
return
}
// hset keeps a set of unique hostnames.
hset := make(map[string]bool)
// insert CN into hset
if b.Cert.Subject.CommonName != "" {
hset[b.Cert.Subject.CommonName] = true
}
// insert all DNS names into hset
for _, h := range b.Cert.DNSNames {
hset[h] = true
}
// convert hset to an array of hostnames
b.Hostnames = make([]string, len(hset))
i := 0
for h := range hset {
b.Hostnames[i] = h
i++
}
}
// Package bundler provides an API for creating certificate bundles,
// which contain a trust chain of certificates. Generally, the bundles
// will also include the private key (but this is not strictly
// required). In this package, a bundle refers to a certificate with
// full trust chain -- all certificates in the chain in one file or
// buffer.
//
// The first step in creating a certificate bundle is to create a
// Bundler. A Bundler must be created from a pre-existing certificate
// authority bundle and an intermediate certificate bundle. Once the
// Bundler is initialised, bundles may be created using a variety of
// methods: from PEM- or DER-encoded files, directly from the relevant
// Go structures, or by starting with the certificate from a remote
// system. These functions return a Bundle value, which may be
// serialised to JSON.
package bundler
......@@ -17,11 +17,7 @@ filegroup(
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/cloudflare/cfssl/certdb/dbconf:all-srcs",
"//vendor/github.com/cloudflare/cfssl/certdb/sql:all-srcs",
],
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["db_config.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/certdb/dbconf",
importpath = "github.com/cloudflare/cfssl/certdb/dbconf",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/jmoiron/sqlx:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package dbconf
import (
"encoding/json"
"errors"
"io/ioutil"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/log"
"github.com/jmoiron/sqlx"
)
// DBConfig contains the database driver name and configuration to be passed to Open
type DBConfig struct {
DriverName string `json:"driver"`
DataSourceName string `json:"data_source"`
}
// LoadFile attempts to load the db configuration file stored at the path
// and returns the configuration. On error, it returns nil.
func LoadFile(path string) (cfg *DBConfig, err error) {
log.Debugf("loading db configuration file from %s", path)
if path == "" {
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid path"))
}
var body []byte
body, err = ioutil.ReadFile(path)
if err != nil {
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("could not read configuration file"))
}
cfg = &DBConfig{}
err = json.Unmarshal(body, &cfg)
if err != nil {
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy,
errors.New("failed to unmarshal configuration: "+err.Error()))
}
if cfg.DataSourceName == "" || cfg.DriverName == "" {
return nil, cferr.Wrap(cferr.PolicyError, cferr.InvalidPolicy, errors.New("invalid db configuration"))
}
return
}
// DBFromConfig opens a sql.DB from settings in a db config file
func DBFromConfig(path string) (db *sqlx.DB, err error) {
var dbCfg *DBConfig
dbCfg, err = LoadFile(path)
if err != nil {
return nil, err
}
return sqlx.Open(dbCfg.DriverName, dbCfg.DataSourceName)
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["database_accessor.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/certdb/sql",
importpath = "github.com/cloudflare/cfssl/certdb/sql",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/cloudflare/cfssl/certdb:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/jmoiron/sqlx:go_default_library",
"//vendor/github.com/kisielk/sqlstruct:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package sql
import (
"errors"
"fmt"
"time"
"github.com/cloudflare/cfssl/certdb"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/jmoiron/sqlx"
"github.com/kisielk/sqlstruct"
)
// Match to sqlx
func init() {
sqlstruct.TagName = "db"
}
const (
insertSQL = `
INSERT INTO certificates (serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem)
VALUES (:serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem);`
selectSQL = `
SELECT %s FROM certificates
WHERE (serial_number = ? AND authority_key_identifier = ?);`
selectAllUnexpiredSQL = `
SELECT %s FROM certificates
WHERE CURRENT_TIMESTAMP < expiry;`
selectAllRevokedAndUnexpiredWithLabelSQL = `
SELECT %s FROM certificates
WHERE CURRENT_TIMESTAMP < expiry AND status='revoked' AND ca_label= ?;`
selectAllRevokedAndUnexpiredSQL = `
SELECT %s FROM certificates
WHERE CURRENT_TIMESTAMP < expiry AND status='revoked';`
updateRevokeSQL = `
UPDATE certificates
SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason=:reason
WHERE (serial_number = :serial_number AND authority_key_identifier = :authority_key_identifier);`
insertOCSPSQL = `
INSERT INTO ocsp_responses (serial_number, authority_key_identifier, body, expiry)
VALUES (:serial_number, :authority_key_identifier, :body, :expiry);`
updateOCSPSQL = `
UPDATE ocsp_responses
SET body = :body, expiry = :expiry
WHERE (serial_number = :serial_number AND authority_key_identifier = :authority_key_identifier);`
selectAllUnexpiredOCSPSQL = `
SELECT %s FROM ocsp_responses
WHERE CURRENT_TIMESTAMP < expiry;`
selectOCSPSQL = `
SELECT %s FROM ocsp_responses
WHERE (serial_number = ? AND authority_key_identifier = ?);`
)
// Accessor implements certdb.Accessor interface.
type Accessor struct {
db *sqlx.DB
}
func wrapSQLError(err error) error {
if err != nil {
return cferr.Wrap(cferr.CertStoreError, cferr.Unknown, err)
}
return nil
}
func (d *Accessor) checkDB() error {
if d.db == nil {
return cferr.Wrap(cferr.CertStoreError, cferr.Unknown,
errors.New("unknown db object, please check SetDB method"))
}
return nil
}
// NewAccessor returns a new Accessor.
func NewAccessor(db *sqlx.DB) *Accessor {
return &Accessor{db: db}
}
// SetDB changes the underlying sql.DB object Accessor is manipulating.
func (d *Accessor) SetDB(db *sqlx.DB) {
d.db = db
return
}
// InsertCertificate puts a certdb.CertificateRecord into db.
func (d *Accessor) InsertCertificate(cr certdb.CertificateRecord) error {
err := d.checkDB()
if err != nil {
return err
}
res, err := d.db.NamedExec(insertSQL, &certdb.CertificateRecord{
Serial: cr.Serial,
AKI: cr.AKI,
CALabel: cr.CALabel,
Status: cr.Status,
Reason: cr.Reason,
Expiry: cr.Expiry.UTC(),
RevokedAt: cr.RevokedAt.UTC(),
PEM: cr.PEM,
})
if err != nil {
return wrapSQLError(err)
}
numRowsAffected, err := res.RowsAffected()
if numRowsAffected == 0 {
return cferr.Wrap(cferr.CertStoreError, cferr.InsertionFailed, fmt.Errorf("failed to insert the certificate record"))
}
if numRowsAffected != 1 {
return wrapSQLError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected))
}
return err
}
// GetCertificate gets a certdb.CertificateRecord indexed by serial.
func (d *Accessor) GetCertificate(serial, aki string) (crs []certdb.CertificateRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectSQL), sqlstruct.Columns(certdb.CertificateRecord{})), serial, aki)
if err != nil {
return nil, wrapSQLError(err)
}
return crs, nil
}
// GetUnexpiredCertificates gets all unexpired certificate from db.
func (d *Accessor) GetUnexpiredCertificates() (crs []certdb.CertificateRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectAllUnexpiredSQL), sqlstruct.Columns(certdb.CertificateRecord{})))
if err != nil {
return nil, wrapSQLError(err)
}
return crs, nil
}
// GetRevokedAndUnexpiredCertificates gets all revoked and unexpired certificate from db (for CRLs).
func (d *Accessor) GetRevokedAndUnexpiredCertificates() (crs []certdb.CertificateRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectAllRevokedAndUnexpiredSQL), sqlstruct.Columns(certdb.CertificateRecord{})))
if err != nil {
return nil, wrapSQLError(err)
}
return crs, nil
}
// GetRevokedAndUnexpiredCertificatesByLabel gets all revoked and unexpired certificate from db (for CRLs) with specified ca_label.
func (d *Accessor) GetRevokedAndUnexpiredCertificatesByLabel(label string) (crs []certdb.CertificateRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectAllRevokedAndUnexpiredWithLabelSQL), sqlstruct.Columns(certdb.CertificateRecord{})), label)
if err != nil {
return nil, wrapSQLError(err)
}
return crs, nil
}
// RevokeCertificate updates a certificate with a given serial number and marks it revoked.
func (d *Accessor) RevokeCertificate(serial, aki string, reasonCode int) error {
err := d.checkDB()
if err != nil {
return err
}
result, err := d.db.NamedExec(updateRevokeSQL, &certdb.CertificateRecord{
AKI: aki,
Reason: reasonCode,
Serial: serial,
})
if err != nil {
return wrapSQLError(err)
}
numRowsAffected, err := result.RowsAffected()
if numRowsAffected == 0 {
return cferr.Wrap(cferr.CertStoreError, cferr.RecordNotFound, fmt.Errorf("failed to revoke the certificate: certificate not found"))
}
if numRowsAffected != 1 {
return wrapSQLError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected))
}
return err
}
// InsertOCSP puts a new certdb.OCSPRecord into the db.
func (d *Accessor) InsertOCSP(rr certdb.OCSPRecord) error {
err := d.checkDB()
if err != nil {
return err
}
result, err := d.db.NamedExec(insertOCSPSQL, &certdb.OCSPRecord{
AKI: rr.AKI,
Body: rr.Body,
Expiry: rr.Expiry.UTC(),
Serial: rr.Serial,
})
if err != nil {
return wrapSQLError(err)
}
numRowsAffected, err := result.RowsAffected()
if numRowsAffected == 0 {
return cferr.Wrap(cferr.CertStoreError, cferr.InsertionFailed, fmt.Errorf("failed to insert the OCSP record"))
}
if numRowsAffected != 1 {
return wrapSQLError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected))
}
return err
}
// GetOCSP retrieves a certdb.OCSPRecord from db by serial.
func (d *Accessor) GetOCSP(serial, aki string) (ors []certdb.OCSPRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&ors, fmt.Sprintf(d.db.Rebind(selectOCSPSQL), sqlstruct.Columns(certdb.OCSPRecord{})), serial, aki)
if err != nil {
return nil, wrapSQLError(err)
}
return ors, nil
}
// GetUnexpiredOCSPs retrieves all unexpired certdb.OCSPRecord from db.
func (d *Accessor) GetUnexpiredOCSPs() (ors []certdb.OCSPRecord, err error) {
err = d.checkDB()
if err != nil {
return nil, err
}
err = d.db.Select(&ors, fmt.Sprintf(d.db.Rebind(selectAllUnexpiredOCSPSQL), sqlstruct.Columns(certdb.OCSPRecord{})))
if err != nil {
return nil, wrapSQLError(err)
}
return ors, nil
}
// UpdateOCSP updates a ocsp response record with a given serial number.
func (d *Accessor) UpdateOCSP(serial, aki, body string, expiry time.Time) error {
err := d.checkDB()
if err != nil {
return err
}
result, err := d.db.NamedExec(updateOCSPSQL, &certdb.OCSPRecord{
AKI: aki,
Body: body,
Expiry: expiry.UTC(),
Serial: serial,
})
if err != nil {
return wrapSQLError(err)
}
numRowsAffected, err := result.RowsAffected()
if numRowsAffected == 0 {
return cferr.Wrap(cferr.CertStoreError, cferr.RecordNotFound, fmt.Errorf("failed to update the OCSP record"))
}
if numRowsAffected != 1 {
return wrapSQLError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected))
}
return err
}
// UpsertOCSP update a ocsp response record with a given serial number,
// or insert the record if it doesn't yet exist in the db
// Implementation note:
// We didn't implement 'upsert' with SQL statement and we lost race condition
// prevention provided by underlying DBMS.
// Reasoning:
// 1. it's diffcult to support multiple DBMS backends in the same time, the
// SQL syntax differs from one to another.
// 2. we don't need a strict simultaneous consistency between OCSP and certificate
// status. It's OK that a OCSP response still shows 'good' while the
// corresponding certificate is being revoked seconds ago, as long as the OCSP
// response catches up to be eventually consistent (within hours to days).
// Write race condition between OCSP writers on OCSP table is not a problem,
// since we don't have write race condition on Certificate table and OCSP
// writers should periodically use Certificate table to update OCSP table
// to catch up.
func (d *Accessor) UpsertOCSP(serial, aki, body string, expiry time.Time) error {
err := d.checkDB()
if err != nil {
return err
}
result, err := d.db.NamedExec(updateOCSPSQL, &certdb.OCSPRecord{
AKI: aki,
Body: body,
Expiry: expiry.UTC(),
Serial: serial,
})
if err != nil {
return wrapSQLError(err)
}
numRowsAffected, err := result.RowsAffected()
if numRowsAffected == 0 {
return d.InsertOCSP(certdb.OCSPRecord{Serial: serial, AKI: aki, Body: body, Expiry: expiry})
}
if numRowsAffected != 1 {
return wrapSQLError(fmt.Errorf("%d rows are affected, should be 1 row", numRowsAffected))
}
return err
}
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["certinfo.go"],
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/certinfo",
importpath = "github.com/cloudflare/cfssl/certinfo",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/cloudflare/cfssl/helpers:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package certinfo
import (
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"
"io/ioutil"
"net"
"strings"
"time"
"github.com/cloudflare/cfssl/helpers"
)
// Certificate represents a JSON description of an X.509 certificate.
type Certificate struct {
Subject Name `json:"subject,omitempty"`
Issuer Name `json:"issuer,omitempty"`
SerialNumber string `json:"serial_number,omitempty"`
SANs []string `json:"sans,omitempty"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
SignatureAlgorithm string `json:"sigalg"`
AKI string `json:"authority_key_id"`
SKI string `json:"subject_key_id"`
RawPEM string `json:"pem"`
}
// Name represents a JSON description of a PKIX Name
type Name struct {
CommonName string `json:"common_name,omitempty"`
SerialNumber string `json:"serial_number,omitempty"`
Country string `json:"country,omitempty"`
Organization string `json:"organization,omitempty"`
OrganizationalUnit string `json:"organizational_unit,omitempty"`
Locality string `json:"locality,omitempty"`
Province string `json:"province,omitempty"`
StreetAddress string `json:"street_address,omitempty"`
PostalCode string `json:"postal_code,omitempty"`
Names []interface{} `json:"names,omitempty"`
//ExtraNames []interface{} `json:"extra_names,omitempty"`
}
// ParseName parses a new name from a *pkix.Name
func ParseName(name pkix.Name) Name {
n := Name{
CommonName: name.CommonName,
SerialNumber: name.SerialNumber,
Country: strings.Join(name.Country, ","),
Organization: strings.Join(name.Organization, ","),
OrganizationalUnit: strings.Join(name.OrganizationalUnit, ","),
Locality: strings.Join(name.Locality, ","),
Province: strings.Join(name.Province, ","),
StreetAddress: strings.Join(name.StreetAddress, ","),
PostalCode: strings.Join(name.PostalCode, ","),
}
for i := range name.Names {
n.Names = append(n.Names, name.Names[i].Value)
}
// ExtraNames aren't supported in Go 1.4
// for i := range name.ExtraNames {
// n.ExtraNames = append(n.ExtraNames, name.ExtraNames[i].Value)
// }
return n
}
func formatKeyID(id []byte) string {
var s string
for i, c := range id {
if i > 0 {
s += ":"
}
s += fmt.Sprintf("%02X", c)
}
return s
}
// ParseCertificate parses an x509 certificate.
func ParseCertificate(cert *x509.Certificate) *Certificate {
c := &Certificate{
RawPEM: string(helpers.EncodeCertificatePEM(cert)),
SignatureAlgorithm: helpers.SignatureString(cert.SignatureAlgorithm),
NotBefore: cert.NotBefore,
NotAfter: cert.NotAfter,
Subject: ParseName(cert.Subject),
Issuer: ParseName(cert.Issuer),
SANs: cert.DNSNames,
AKI: formatKeyID(cert.AuthorityKeyId),
SKI: formatKeyID(cert.SubjectKeyId),
SerialNumber: cert.SerialNumber.String(),
}
for _, ip := range cert.IPAddresses {
c.SANs = append(c.SANs, ip.String())
}
return c
}
// ParseCertificateFile parses x509 certificate file.
func ParseCertificateFile(certFile string) (*Certificate, error) {
certPEM, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}
return ParseCertificatePEM(certPEM)
}
// ParseCertificatePEM parses an x509 certificate PEM.
func ParseCertificatePEM(certPEM []byte) (*Certificate, error) {
cert, err := helpers.ParseCertificatePEM(certPEM)
if err != nil {
return nil, err
}
return ParseCertificate(cert), nil
}
// ParseCSRPEM uses the helper to parse an x509 CSR PEM.
func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
csrObject, err := helpers.ParseCSRPEM(csrPEM)
if err != nil {
return nil, err
}
return csrObject, nil
}
// ParseCSRFile uses the helper to parse an x509 CSR PEM file.
func ParseCSRFile(csrFile string) (*x509.CertificateRequest, error) {
csrPEM, err := ioutil.ReadFile(csrFile)
if err != nil {
return nil, err
}
return ParseCSRPEM(csrPEM)
}
// ParseCertificateDomain parses the certificate served by the given domain.
func ParseCertificateDomain(domain string) (cert *Certificate, err error) {
var host, port string
if host, port, err = net.SplitHostPort(domain); err != nil {
host = domain
port = "443"
}
var conn *tls.Conn
conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 10 * time.Second}, "tcp", net.JoinHostPort(host, port), &tls.Config{InsecureSkipVerify: true})
if err != nil {
return
}
defer conn.Close()
if len(conn.ConnectionState().PeerCertificates) == 0 {
return nil, errors.New("received no server certificates")
}
cert = ParseCertificate(conn.ConnectionState().PeerCertificates[0])
return
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment