mirror of https://github.com/k3s-io/k3s
remove unnecessary vendoring of cmd/cfssl
Duringk3s-v1.15.3dc9def6f93
, which was a revert for7a10073e4a
we forgot to remove the extra thing we started vendoring. So let's drop the cmd/cfssl Change-Id: I308dc41cdb9b990f2b4ab8d59ad0445d5ac02e8c
parent
9e83e6d1cd
commit
437a7333e3
|
@ -4328,35 +4328,6 @@ Exhibit B - “Incompatible With Secondary Licenses” Notice
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/daaku/go.zipexe licensed under: =
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2012-2015 Carlos Castillo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
= vendor/github.com/daaku/go.zipexe/license 2285ef50c2d5083f5bb92af715e5a378
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/davecgh/go-spew licensed under: =
|
||||
|
||||
|
@ -6073,35 +6044,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/GeertJohan/go.rice licensed under: =
|
||||
|
||||
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.
|
||||
= vendor/github.com/GeertJohan/go.rice/LICENSE d5ffb28cde0ca932cbab7e1e1d52c20e
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/ghodss/yaml licensed under: =
|
||||
|
||||
|
@ -8318,387 +8260,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/go-sql-driver/mysql licensed under: =
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
= vendor/github.com/go-sql-driver/mysql/LICENSE 815ca599c9df247a0c7f619bab123dad
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/godbus/dbus licensed under: =
|
||||
|
||||
|
@ -12058,64 +11619,6 @@ limitations under the License.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/jmhodges/clock licensed under: =
|
||||
|
||||
The MIT License (MIT)
|
||||
Copyright © 2014 Jeff Hodges <jeff@somethingsimilar.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the “Software”), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
= vendor/github.com/jmhodges/clock/LICENSE bc91f6bbbc0460d728657b023f3f6596
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/jmoiron/sqlx licensed under: =
|
||||
|
||||
Copyright (c) 2013, Jason Moiron
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
= vendor/github.com/jmoiron/sqlx/LICENSE 4353e10ea313ec47121dd2c7d89ce6b9
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/jonboulle/clockwork licensed under: =
|
||||
|
||||
|
@ -12461,34 +11964,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/kisielk/sqlstruct licensed under: =
|
||||
|
||||
Copyright 2012 Kamil Kisiel <kamil@kamilkisiel.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
= vendor/github.com/kisielk/sqlstruct/LICENSE bc5911f8d3b8034f564b63d7ea436519
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/konsorten/go-windows-terminal-sequences licensed under: =
|
||||
|
||||
|
@ -12541,22 +12016,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/lib/pq licensed under: =
|
||||
|
||||
Copyright (c) 2011-2013, 'pq' Contributors
|
||||
Portions Copyright (C) 2011 Blake Mizerany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
= vendor/github.com/lib/pq/LICENSE.md 0eab29964025b358179aa6d8f7db14bf
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/libopenstorage/openstorage licensed under: =
|
||||
|
||||
|
@ -12954,35 +12413,6 @@ SOFTWARE.
|
|||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/mattn/go-sqlite3 licensed under: =
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Yasuhiro Matsumoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
= vendor/github.com/mattn/go-sqlite3/LICENSE 2b7590a6661bc1940f50329c495898c6
|
||||
================================================================================
|
||||
|
||||
|
||||
================================================================================
|
||||
= vendor/github.com/matttproud/golang_protobuf_extensions licensed under: =
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
8
go.mod
8
go.mod
|
@ -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
|
||||
|
|
16
go.sum
16
go.sum
|
@ -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",
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/example/example
|
||||
/example/example.exe
|
||||
/rice/rice
|
||||
/rice/rice.exe
|
||||
|
||||
*.rice-box.go
|
||||
*.rice-box.syso
|
||||
.wercker
|
|
@ -1,19 +0,0 @@
|
|||
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
|
|
@ -1,4 +0,0 @@
|
|||
Geert-Johan Riemer <geertjohan@geertjohan.net>
|
||||
Paul Maddox <paul.maddox@gmail.com>
|
||||
Vincent Petithory <vincent.petithory@gmail.com>
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,22 +0,0 @@
|
|||
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.
|
|
@ -1,151 +0,0 @@
|
|||
## 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
|
|
@ -1,138 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,337 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package rice
|
||||
|
||||
// Debug can be set to true to enable debugging.
|
||||
var Debug = false
|
|
@ -1,90 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,80 +0,0 @@
|
|||
// 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
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
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] }
|
|
@ -1,252 +0,0 @@
|
|||
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"),
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
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
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,231 +0,0 @@
|
|||
// 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
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,91 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,50 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,6 +0,0 @@
|
|||
package client
|
||||
|
||||
// SignResult is the result of signing a CSR.
|
||||
type SignResult struct {
|
||||
Certificate []byte `json:"certificate"`
|
||||
}
|
|
@ -1,370 +0,0 @@
|
|||
// Package client implements a Go client for CFSSL API commands.
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
stderr "errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/api"
|
||||
"github.com/cloudflare/cfssl/auth"
|
||||
"github.com/cloudflare/cfssl/errors"
|
||||
"github.com/cloudflare/cfssl/info"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
)
|
||||
|
||||
// A server points to a single remote CFSSL instance.
|
||||
type server struct {
|
||||
URL string
|
||||
TLSConfig *tls.Config
|
||||
reqModifier func(*http.Request, []byte)
|
||||
RequestTimeout time.Duration
|
||||
proxy func(*http.Request) (*url.URL, error)
|
||||
}
|
||||
|
||||
// A Remote points to at least one (but possibly multiple) remote
|
||||
// CFSSL instances. It must be able to perform a authenticated and
|
||||
// unauthenticated certificate signing requests, return information
|
||||
// about the CA on the other end, and return a list of the hosts that
|
||||
// are used by the remote.
|
||||
type Remote interface {
|
||||
AuthSign(req, id []byte, provider auth.Provider) ([]byte, error)
|
||||
Sign(jsonData []byte) ([]byte, error)
|
||||
Info(jsonData []byte) (*info.Resp, error)
|
||||
Hosts() []string
|
||||
SetReqModifier(func(*http.Request, []byte))
|
||||
SetRequestTimeout(d time.Duration)
|
||||
SetProxy(func(*http.Request) (*url.URL, error))
|
||||
}
|
||||
|
||||
// NewServer sets up a new server target. The address should be of
|
||||
// The format [protocol:]name[:port] of the remote CFSSL instance.
|
||||
// If no protocol is given http is default. If no port
|
||||
// is specified, the CFSSL default port (8888) is used. If the name is
|
||||
// a comma-separated list of hosts, an ordered group will be returned.
|
||||
func NewServer(addr string) Remote {
|
||||
return NewServerTLS(addr, nil)
|
||||
}
|
||||
|
||||
// NewServerTLS is the TLS version of NewServer
|
||||
func NewServerTLS(addr string, tlsConfig *tls.Config) Remote {
|
||||
addrs := strings.Split(addr, ",")
|
||||
|
||||
var remote Remote
|
||||
|
||||
if len(addrs) > 1 {
|
||||
remote, _ = NewGroup(addrs, tlsConfig, StrategyOrderedList)
|
||||
} else {
|
||||
u, err := normalizeURL(addrs[0])
|
||||
if err != nil {
|
||||
log.Errorf("bad url: %v", err)
|
||||
return nil
|
||||
}
|
||||
srv := newServer(u, tlsConfig)
|
||||
if srv != nil {
|
||||
remote = srv
|
||||
}
|
||||
}
|
||||
return remote
|
||||
}
|
||||
|
||||
func (srv *server) Hosts() []string {
|
||||
return []string{srv.URL}
|
||||
}
|
||||
|
||||
func (srv *server) SetReqModifier(mod func(*http.Request, []byte)) {
|
||||
srv.reqModifier = mod
|
||||
}
|
||||
|
||||
func (srv *server) SetRequestTimeout(timeout time.Duration) {
|
||||
srv.RequestTimeout = timeout
|
||||
}
|
||||
|
||||
func (srv *server) SetProxy(proxy func(*http.Request) (*url.URL, error)) {
|
||||
srv.proxy = proxy
|
||||
}
|
||||
|
||||
func newServer(u *url.URL, tlsConfig *tls.Config) *server {
|
||||
URL := u.String()
|
||||
return &server{
|
||||
URL: URL,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *server) getURL(endpoint string) string {
|
||||
return fmt.Sprintf("%s/api/v1/cfssl/%s", srv.URL, endpoint)
|
||||
}
|
||||
|
||||
func (srv *server) createTransport() *http.Transport {
|
||||
// Start with defaults from http.DefaultTransport
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
// Setup HTTPS client
|
||||
tlsConfig := srv.TLSConfig
|
||||
tlsConfig.BuildNameToCertificate()
|
||||
transport.TLSClientConfig = tlsConfig
|
||||
// Setup Proxy
|
||||
if srv.proxy != nil {
|
||||
transport.Proxy = srv.proxy
|
||||
}
|
||||
return transport
|
||||
}
|
||||
|
||||
// post connects to the remote server and returns a Response struct
|
||||
func (srv *server) post(url string, jsonData []byte) (*api.Response, error) {
|
||||
var resp *http.Response
|
||||
var err error
|
||||
client := &http.Client{}
|
||||
if srv.TLSConfig != nil {
|
||||
client.Transport = srv.createTransport()
|
||||
}
|
||||
if srv.RequestTimeout != 0 {
|
||||
client.Timeout = srv.RequestTimeout
|
||||
}
|
||||
req, err := http.NewRequest("POST", url, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed POST to %s: %v", url, err)
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err)
|
||||
}
|
||||
req.Close = true
|
||||
req.Header.Set("content-type", "application/json")
|
||||
if srv.reqModifier != nil {
|
||||
srv.reqModifier(req, jsonData)
|
||||
}
|
||||
resp, err = client.Do(req)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed POST to %s: %v", url, err)
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, err)
|
||||
}
|
||||
defer req.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.IOError, err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Errorf("http error with %s", url)
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New(string(body)))
|
||||
}
|
||||
|
||||
var response api.Response
|
||||
err = json.Unmarshal(body, &response)
|
||||
if err != nil {
|
||||
log.Debug("Unable to parse response body:", string(body))
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
|
||||
}
|
||||
|
||||
if !response.Success || response.Result == nil {
|
||||
if len(response.Errors) > 0 {
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.ServerRequestFailed, stderr.New(response.Errors[0].Message))
|
||||
}
|
||||
return nil, errors.New(errors.APIClientError, errors.ServerRequestFailed)
|
||||
}
|
||||
|
||||
return &response, nil
|
||||
}
|
||||
|
||||
// AuthSign fills out an authenticated signing request to the server,
|
||||
// receiving a certificate or error in response.
|
||||
// It takes the serialized JSON request to send, remote address and
|
||||
// authentication provider.
|
||||
func (srv *server) AuthSign(req, id []byte, provider auth.Provider) ([]byte, error) {
|
||||
return srv.authReq(req, id, provider, "sign")
|
||||
}
|
||||
|
||||
// AuthInfo fills out an authenticated info request to the server,
|
||||
// receiving a certificate or error in response.
|
||||
// It takes the serialized JSON request to send, remote address and
|
||||
// authentication provider.
|
||||
func (srv *server) AuthInfo(req, id []byte, provider auth.Provider) ([]byte, error) {
|
||||
return srv.authReq(req, id, provider, "info")
|
||||
}
|
||||
|
||||
// authReq is the common logic for AuthSign and AuthInfo -- perform the given
|
||||
// request, and return the resultant certificate.
|
||||
// The target is either 'sign' or 'info'.
|
||||
func (srv *server) authReq(req, ID []byte, provider auth.Provider, target string) ([]byte, error) {
|
||||
url := srv.getURL("auth" + target)
|
||||
|
||||
token, err := provider.Token(req)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.AuthenticationFailure, err)
|
||||
}
|
||||
|
||||
aReq := &auth.AuthenticatedRequest{
|
||||
Timestamp: time.Now().Unix(),
|
||||
RemoteAddress: ID,
|
||||
Token: token,
|
||||
Request: req,
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(aReq)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.JSONError, err)
|
||||
}
|
||||
|
||||
response, err := srv.post(url, jsonData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, ok := response.Result.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.New(errors.APIClientError, errors.JSONError)
|
||||
}
|
||||
|
||||
cert, ok := result["certificate"].(string)
|
||||
if !ok {
|
||||
return nil, errors.New(errors.APIClientError, errors.JSONError)
|
||||
}
|
||||
|
||||
return []byte(cert), nil
|
||||
}
|
||||
|
||||
// Sign sends a signature request to the remote CFSSL server,
|
||||
// receiving a signed certificate or an error in response.
|
||||
// It takes the serialized JSON request to send.
|
||||
func (srv *server) Sign(jsonData []byte) ([]byte, error) {
|
||||
return srv.request(jsonData, "sign")
|
||||
}
|
||||
|
||||
// Info sends an info request to the remote CFSSL server, receiving a
|
||||
// response or an error in response.
|
||||
// It takes the serialized JSON request to send.
|
||||
func (srv *server) Info(jsonData []byte) (*info.Resp, error) {
|
||||
res, err := srv.getResultMap(jsonData, "info")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := new(info.Resp)
|
||||
|
||||
if val, ok := res["certificate"]; ok {
|
||||
info.Certificate = val.(string)
|
||||
}
|
||||
var usages []interface{}
|
||||
if val, ok := res["usages"]; ok && val != nil {
|
||||
usages = val.([]interface{})
|
||||
}
|
||||
if val, ok := res["expiry"]; ok && val != nil {
|
||||
info.ExpiryString = val.(string)
|
||||
}
|
||||
|
||||
info.Usage = make([]string, len(usages))
|
||||
for i, s := range usages {
|
||||
info.Usage[i] = s.(string)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (srv *server) getResultMap(jsonData []byte, target string) (result map[string]interface{}, err error) {
|
||||
url := srv.getURL(target)
|
||||
response, err := srv.post(url, jsonData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
result, ok := response.Result.(map[string]interface{})
|
||||
if !ok {
|
||||
err = errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response is formatted improperly"))
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// request performs the common logic for Sign and Info, performing the actual
|
||||
// request and returning the resultant certificate.
|
||||
func (srv *server) request(jsonData []byte, target string) ([]byte, error) {
|
||||
result, err := srv.getResultMap(jsonData, target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert := result["certificate"].(string)
|
||||
if cert != "" {
|
||||
return []byte(cert), nil
|
||||
}
|
||||
|
||||
return nil, errors.Wrap(errors.APIClientError, errors.ClientHTTPError, stderr.New("response doesn't contain certificate."))
|
||||
}
|
||||
|
||||
// AuthRemote acts as a Remote with a default Provider for AuthSign.
|
||||
type AuthRemote struct {
|
||||
Remote
|
||||
provider auth.Provider
|
||||
}
|
||||
|
||||
// NewAuthServer sets up a new auth server target with an addr
|
||||
// in the same format at NewServer and a default authentication provider to
|
||||
// use for Sign requests.
|
||||
func NewAuthServer(addr string, tlsConfig *tls.Config, provider auth.Provider) *AuthRemote {
|
||||
return &AuthRemote{
|
||||
Remote: NewServerTLS(addr, tlsConfig),
|
||||
provider: provider,
|
||||
}
|
||||
}
|
||||
|
||||
// Sign is overloaded to perform an AuthSign request using the default auth provider.
|
||||
func (ar *AuthRemote) Sign(req []byte) ([]byte, error) {
|
||||
return ar.AuthSign(req, nil, ar.provider)
|
||||
}
|
||||
|
||||
// nomalizeURL checks for http/https protocol, appends "http" as default protocol if not defiend in url
|
||||
func normalizeURL(addr string) (*url.URL, error) {
|
||||
addr = strings.TrimSpace(addr)
|
||||
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u.Opaque != "" {
|
||||
u.Host = net.JoinHostPort(u.Scheme, u.Opaque)
|
||||
u.Opaque = ""
|
||||
} else if u.Path != "" && !strings.Contains(u.Path, ":") {
|
||||
u.Host = net.JoinHostPort(u.Path, "8888")
|
||||
u.Path = ""
|
||||
} else if u.Scheme == "" {
|
||||
u.Host = u.Path
|
||||
u.Path = ""
|
||||
}
|
||||
|
||||
if u.Scheme != "https" {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
|
||||
_, port, err := net.SplitHostPort(u.Host)
|
||||
if err != nil {
|
||||
_, port, err = net.SplitHostPort(u.Host + ":8888")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if port != "" {
|
||||
_, err = strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return u, nil
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,93 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,102 +0,0 @@
|
|||
// 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"},
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,319 +0,0 @@
|
|||
// 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
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
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"},
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,121 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,61 +0,0 @@
|
|||
// 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"}}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,113 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,137 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,76 +0,0 @@
|
|||
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"},
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,294 +0,0 @@
|
|||
// 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)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,186 +0,0 @@
|
|||
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++
|
||||
}
|
||||
}
|
|
@ -1,843 +0,0 @@
|
|||
// Package bundler implements certificate bundling functionality for
|
||||
// CFSSL.
|
||||
package bundler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
goerr "errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/errors"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/ubiquity"
|
||||
)
|
||||
|
||||
// IntermediateStash contains the path to the directory where
|
||||
// downloaded intermediates should be saved.
|
||||
// When unspecified, downloaded intermediates are not saved.
|
||||
var IntermediateStash string
|
||||
|
||||
// BundleFlavor is named optimization strategy on certificate chain selection when bundling.
|
||||
type BundleFlavor string
|
||||
|
||||
const (
|
||||
// Optimal means the shortest chain with newest intermediates and
|
||||
// the most advanced crypto.
|
||||
Optimal BundleFlavor = "optimal"
|
||||
|
||||
// Ubiquitous is aimed to provide the chain which is accepted
|
||||
// by the most platforms.
|
||||
Ubiquitous BundleFlavor = "ubiquitous"
|
||||
|
||||
// Force means the bundler only verfiies the input as a valid bundle, not optimization is done.
|
||||
Force BundleFlavor = "force"
|
||||
)
|
||||
|
||||
const (
|
||||
sha2Warning = "The bundle contains certificates signed with advanced hash functions such as SHA2, which are problematic for certain operating systems, e.g. Windows XP SP2."
|
||||
ecdsaWarning = "The bundle contains ECDSA signatures, which are problematic for certain operating systems, e.g. Windows XP, Android 2.2 and Android 2.3."
|
||||
expiringWarningStub = "The bundle is expiring within 30 days."
|
||||
untrustedWarningStub = "The bundle may not be trusted by the following platform(s):"
|
||||
ubiquityWarning = "Unable to measure bundle ubiquity: No platform metadata present."
|
||||
)
|
||||
|
||||
// A Bundler contains the certificate pools for producing certificate
|
||||
// bundles. It contains any intermediates and root certificates that
|
||||
// should be used.
|
||||
type Bundler struct {
|
||||
RootPool *x509.CertPool
|
||||
IntermediatePool *x509.CertPool
|
||||
KnownIssuers map[string]bool
|
||||
opts options
|
||||
}
|
||||
|
||||
type options struct {
|
||||
keyUsages []x509.ExtKeyUsage
|
||||
}
|
||||
|
||||
var defaultOptions = options{
|
||||
keyUsages: []x509.ExtKeyUsage{
|
||||
x509.ExtKeyUsageAny,
|
||||
},
|
||||
}
|
||||
|
||||
// An Option sets options such as allowed key usages, etc.
|
||||
type Option func(*options)
|
||||
|
||||
// WithKeyUsages lets you set which Extended Key Usage values are acceptable. By
|
||||
// default x509.ExtKeyUsageAny will be used.
|
||||
func WithKeyUsages(usages ...x509.ExtKeyUsage) Option {
|
||||
return func(o *options) {
|
||||
o.keyUsages = usages
|
||||
}
|
||||
}
|
||||
|
||||
// NewBundler creates a new Bundler from the files passed in; these
|
||||
// files should contain a list of valid root certificates and a list
|
||||
// of valid intermediate certificates, respectively.
|
||||
func NewBundler(caBundleFile, intBundleFile string, opt ...Option) (*Bundler, error) {
|
||||
var caBundle, intBundle []byte
|
||||
var err error
|
||||
|
||||
if caBundleFile != "" {
|
||||
log.Debug("Loading CA bundle: ", caBundleFile)
|
||||
caBundle, err = ioutil.ReadFile(caBundleFile)
|
||||
if err != nil {
|
||||
log.Errorf("root bundle failed to load: %v", err)
|
||||
return nil, errors.Wrap(errors.RootError, errors.ReadFailed, err)
|
||||
}
|
||||
}
|
||||
|
||||
if intBundleFile != "" {
|
||||
log.Debug("Loading Intermediate bundle: ", intBundleFile)
|
||||
intBundle, err = ioutil.ReadFile(intBundleFile)
|
||||
if err != nil {
|
||||
log.Errorf("intermediate bundle failed to load: %v", err)
|
||||
return nil, errors.Wrap(errors.IntermediatesError, errors.ReadFailed, err)
|
||||
}
|
||||
}
|
||||
|
||||
if IntermediateStash != "" {
|
||||
if _, err = os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
|
||||
log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
|
||||
err = os.MkdirAll(IntermediateStash, 0755)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create intermediate stash directory %s: %v",
|
||||
IntermediateStash, err)
|
||||
return nil, err
|
||||
}
|
||||
log.Infof("intermediate stash directory %s created", IntermediateStash)
|
||||
}
|
||||
}
|
||||
|
||||
return NewBundlerFromPEM(caBundle, intBundle, opt...)
|
||||
|
||||
}
|
||||
|
||||
// NewBundlerFromPEM creates a new Bundler from PEM-encoded root certificates and
|
||||
// intermediate certificates.
|
||||
// If caBundlePEM is nil, the resulting Bundler can only do "Force" bundle.
|
||||
func NewBundlerFromPEM(caBundlePEM, intBundlePEM []byte, opt ...Option) (*Bundler, error) {
|
||||
opts := defaultOptions
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
log.Debug("parsing root certificates from PEM")
|
||||
roots, err := helpers.ParseCertificatesPEM(caBundlePEM)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse root bundle: %v", err)
|
||||
return nil, errors.New(errors.RootError, errors.ParseFailed)
|
||||
}
|
||||
|
||||
log.Debug("parse intermediate certificates from PEM")
|
||||
intermediates, err := helpers.ParseCertificatesPEM(intBundlePEM)
|
||||
if err != nil {
|
||||
log.Errorf("failed to parse intermediate bundle: %v", err)
|
||||
return nil, errors.New(errors.IntermediatesError, errors.ParseFailed)
|
||||
}
|
||||
|
||||
b := &Bundler{
|
||||
KnownIssuers: map[string]bool{},
|
||||
IntermediatePool: x509.NewCertPool(),
|
||||
opts: opts,
|
||||
}
|
||||
|
||||
log.Debug("building certificate pools")
|
||||
|
||||
// RootPool will be nil if caBundlePEM is nil, also
|
||||
// that translates to caBundleFile is "".
|
||||
// Systems root store will be used.
|
||||
if caBundlePEM != nil {
|
||||
b.RootPool = x509.NewCertPool()
|
||||
}
|
||||
|
||||
for _, c := range roots {
|
||||
b.RootPool.AddCert(c)
|
||||
b.KnownIssuers[string(c.Signature)] = true
|
||||
}
|
||||
|
||||
for _, c := range intermediates {
|
||||
b.IntermediatePool.AddCert(c)
|
||||
b.KnownIssuers[string(c.Signature)] = true
|
||||
}
|
||||
|
||||
log.Debug("bundler set up")
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// VerifyOptions generates an x509 VerifyOptions structure that can be
|
||||
// used for verifying certificates.
|
||||
func (b *Bundler) VerifyOptions() x509.VerifyOptions {
|
||||
return x509.VerifyOptions{
|
||||
Roots: b.RootPool,
|
||||
Intermediates: b.IntermediatePool,
|
||||
KeyUsages: b.opts.keyUsages,
|
||||
}
|
||||
}
|
||||
|
||||
// BundleFromFile takes a set of files containing the PEM-encoded leaf certificate
|
||||
// (optionally along with some intermediate certs), the PEM-encoded private key
|
||||
// and returns the bundle built from that key and the certificate(s).
|
||||
func (b *Bundler) BundleFromFile(bundleFile, keyFile string, flavor BundleFlavor, password string) (*Bundle, error) {
|
||||
log.Debug("Loading Certificate: ", bundleFile)
|
||||
certsRaw, err := ioutil.ReadFile(bundleFile)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(errors.CertificateError, errors.ReadFailed, err)
|
||||
}
|
||||
|
||||
var keyPEM []byte
|
||||
// Load private key PEM only if a file is given
|
||||
if keyFile != "" {
|
||||
log.Debug("Loading private key: ", keyFile)
|
||||
keyPEM, err = ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read private key: ", err)
|
||||
return nil, errors.Wrap(errors.PrivateKeyError, errors.ReadFailed, err)
|
||||
}
|
||||
if len(keyPEM) == 0 {
|
||||
log.Debug("key is empty")
|
||||
return nil, errors.Wrap(errors.PrivateKeyError, errors.DecodeFailed, err)
|
||||
}
|
||||
}
|
||||
|
||||
return b.BundleFromPEMorDER(certsRaw, keyPEM, flavor, password)
|
||||
}
|
||||
|
||||
// BundleFromPEMorDER builds a certificate bundle from the set of byte
|
||||
// slices containing the PEM or DER-encoded certificate(s), private key.
|
||||
func (b *Bundler) BundleFromPEMorDER(certsRaw, keyPEM []byte, flavor BundleFlavor, password string) (*Bundle, error) {
|
||||
log.Debug("bundling from PEM files")
|
||||
var key crypto.Signer
|
||||
var err error
|
||||
if len(keyPEM) != 0 {
|
||||
key, err = helpers.ParsePrivateKeyPEM(keyPEM)
|
||||
if err != nil {
|
||||
log.Debugf("failed to parse private key: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
certs, err := helpers.ParseCertificatesPEM(certsRaw)
|
||||
if err != nil {
|
||||
// If PEM doesn't work try DER
|
||||
var keyDER crypto.Signer
|
||||
var errDER error
|
||||
certs, keyDER, errDER = helpers.ParseCertificatesDER(certsRaw, password)
|
||||
// Only use DER key if no key read from file
|
||||
if key == nil && keyDER != nil {
|
||||
key = keyDER
|
||||
}
|
||||
if errDER != nil {
|
||||
log.Debugf("failed to parse certificates: %v", err)
|
||||
// If neither parser works pass along PEM error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
if len(certs) == 0 {
|
||||
log.Debugf("no certificates found")
|
||||
return nil, errors.New(errors.CertificateError, errors.DecodeFailed)
|
||||
}
|
||||
|
||||
log.Debugf("bundle ready")
|
||||
return b.Bundle(certs, key, flavor)
|
||||
}
|
||||
|
||||
// BundleFromRemote fetches the certificate served by the server at
|
||||
// serverName (or ip, if the ip argument is not the empty string). It
|
||||
// is expected that the method will be able to make a connection at
|
||||
// port 443. The certificate used by the server in this connection is
|
||||
// used to build the bundle, which will necessarily be keyless.
|
||||
func (b *Bundler) BundleFromRemote(serverName, ip string, flavor BundleFlavor) (*Bundle, error) {
|
||||
config := &tls.Config{
|
||||
RootCAs: b.RootPool,
|
||||
ServerName: serverName,
|
||||
}
|
||||
|
||||
// Dial by IP if present
|
||||
var dialName string
|
||||
if ip != "" {
|
||||
dialName = ip + ":443"
|
||||
} else {
|
||||
dialName = serverName + ":443"
|
||||
}
|
||||
|
||||
log.Debugf("bundling from remote %s", dialName)
|
||||
|
||||
dialer := &net.Dialer{Timeout: time.Duration(5) * time.Second}
|
||||
conn, err := tls.DialWithDialer(dialer, "tcp", dialName, config)
|
||||
var dialError string
|
||||
// If there's an error in tls.Dial, try again with
|
||||
// InsecureSkipVerify to fetch the remote bundle to (re-)bundle
|
||||
// with. If the bundle is indeed not usable (expired, mismatched
|
||||
// hostnames, etc.), report the error. Otherwise, create a
|
||||
// working bundle and insert the tls error in the bundle.Status.
|
||||
if err != nil {
|
||||
log.Debugf("dial failed: %v", err)
|
||||
// record the error msg
|
||||
dialError = fmt.Sprintf("Failed rigid TLS handshake with %s: %v", dialName, err)
|
||||
// dial again with InsecureSkipVerify
|
||||
log.Debugf("try again with InsecureSkipVerify.")
|
||||
config.InsecureSkipVerify = true
|
||||
conn, err = tls.DialWithDialer(dialer, "tcp", dialName, config)
|
||||
if err != nil {
|
||||
log.Debugf("dial with InsecureSkipVerify failed: %v", err)
|
||||
return nil, errors.Wrap(errors.DialError, errors.Unknown, err)
|
||||
}
|
||||
}
|
||||
|
||||
connState := conn.ConnectionState()
|
||||
|
||||
certs := connState.PeerCertificates
|
||||
|
||||
err = conn.VerifyHostname(serverName)
|
||||
if err != nil {
|
||||
log.Debugf("failed to verify hostname: %v", err)
|
||||
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||
}
|
||||
|
||||
// Bundle with remote certs. Inject the initial dial error, if any, to the status reporting.
|
||||
bundle, err := b.Bundle(certs, nil, flavor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if dialError != "" {
|
||||
bundle.Status.Messages = append(bundle.Status.Messages, dialError)
|
||||
}
|
||||
return bundle, err
|
||||
}
|
||||
|
||||
type fetchedIntermediate struct {
|
||||
Cert *x509.Certificate
|
||||
Name string
|
||||
}
|
||||
|
||||
// fetchRemoteCertificate retrieves a single URL pointing to a certificate
|
||||
// and attempts to first parse it as a DER-encoded certificate; if
|
||||
// this fails, it attempts to decode it as a PEM-encoded certificate.
|
||||
func fetchRemoteCertificate(certURL string) (fi *fetchedIntermediate, err error) {
|
||||
log.Debugf("fetching remote certificate: %s", certURL)
|
||||
var resp *http.Response
|
||||
resp, err = http.Get(certURL)
|
||||
if err != nil {
|
||||
log.Debugf("failed HTTP get: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
var certData []byte
|
||||
certData, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read response body: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("attempting to parse certificate as DER")
|
||||
crt, err := x509.ParseCertificate(certData)
|
||||
if err != nil {
|
||||
log.Debugf("attempting to parse certificate as PEM")
|
||||
crt, err = helpers.ParseCertificatePEM(certData)
|
||||
if err != nil {
|
||||
log.Debugf("failed to parse certificate: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Debugf("certificate fetch succeeds")
|
||||
fi = &fetchedIntermediate{Cert: crt, Name: constructCertFileName(crt)}
|
||||
return
|
||||
}
|
||||
|
||||
func reverse(certs []*x509.Certificate) []*x509.Certificate {
|
||||
n := len(certs)
|
||||
if n == 0 {
|
||||
return certs
|
||||
}
|
||||
rcerts := []*x509.Certificate{}
|
||||
for i := n - 1; i >= 0; i-- {
|
||||
rcerts = append(rcerts, certs[i])
|
||||
}
|
||||
return rcerts
|
||||
}
|
||||
|
||||
// Check if the certs form a partial cert chain: every cert verifies
|
||||
// the signature of the one in front of it.
|
||||
func partialVerify(certs []*x509.Certificate) bool {
|
||||
n := len(certs)
|
||||
if n == 0 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < n-1; i++ {
|
||||
if certs[i].CheckSignatureFrom(certs[i+1]) != nil {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func isSelfSigned(cert *x509.Certificate) bool {
|
||||
return cert.CheckSignatureFrom(cert) == nil
|
||||
}
|
||||
|
||||
func isChainRootNode(cert *x509.Certificate) bool {
|
||||
if isSelfSigned(cert) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Bundler) verifyChain(chain []*fetchedIntermediate) bool {
|
||||
// This process will verify if the root of the (partial) chain is in our root pool,
|
||||
// and will fail otherwise.
|
||||
log.Debugf("verifying chain")
|
||||
for vchain := chain[:]; len(vchain) > 0; vchain = vchain[1:] {
|
||||
cert := vchain[0]
|
||||
// If this is a certificate in one of the pools, skip it.
|
||||
if b.KnownIssuers[string(cert.Cert.Signature)] {
|
||||
log.Debugf("certificate is known")
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := cert.Cert.Verify(b.VerifyOptions())
|
||||
if err != nil {
|
||||
log.Debugf("certificate failed verification: %v", err)
|
||||
return false
|
||||
} else if len(chain) == len(vchain) && isChainRootNode(cert.Cert) {
|
||||
// The first certificate in the chain is a root; it shouldn't be stored.
|
||||
log.Debug("looking at root certificate, will not store")
|
||||
continue
|
||||
}
|
||||
|
||||
// leaf cert has an empty name, don't store leaf cert.
|
||||
if cert.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debug("add certificate to intermediate pool:", cert.Name)
|
||||
b.IntermediatePool.AddCert(cert.Cert)
|
||||
b.KnownIssuers[string(cert.Cert.Signature)] = true
|
||||
|
||||
if IntermediateStash != "" {
|
||||
fileName := filepath.Join(IntermediateStash, cert.Name)
|
||||
|
||||
var block = pem.Block{Type: "CERTIFICATE", Bytes: cert.Cert.Raw}
|
||||
|
||||
log.Debugf("write intermediate to stash directory: %s", fileName)
|
||||
// If the write fails, verification should not fail.
|
||||
err = ioutil.WriteFile(fileName, pem.EncodeToMemory(&block), 0644)
|
||||
if err != nil {
|
||||
log.Errorf("failed to write new intermediate: %v", err)
|
||||
} else {
|
||||
log.Info("stashed new intermediate ", cert.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// constructCertFileName returns a uniquely identifying file name for a certificate
|
||||
func constructCertFileName(cert *x509.Certificate) string {
|
||||
// construct the filename as the CN with no period and space
|
||||
name := strings.Replace(cert.Subject.CommonName, ".", "", -1)
|
||||
name = strings.Replace(name, " ", "", -1)
|
||||
|
||||
// add SKI and serial number as extra identifier
|
||||
name += fmt.Sprintf("_%x", cert.SubjectKeyId)
|
||||
name += fmt.Sprintf("_%x", cert.SerialNumber.Bytes())
|
||||
|
||||
name += ".crt"
|
||||
return name
|
||||
}
|
||||
|
||||
// fetchIntermediates goes through each of the URLs in the AIA "Issuing
|
||||
// CA" extensions and fetches those certificates. If those
|
||||
// certificates are not present in either the root pool or
|
||||
// intermediate pool, the certificate is saved to file and added to
|
||||
// the list of intermediates to be used for verification. This will
|
||||
// not add any new certificates to the root pool; if the ultimate
|
||||
// issuer is not trusted, fetching the certicate here will not change
|
||||
// that.
|
||||
func (b *Bundler) fetchIntermediates(certs []*x509.Certificate) (err error) {
|
||||
if IntermediateStash != "" {
|
||||
log.Debugf("searching intermediates")
|
||||
if _, err := os.Stat(IntermediateStash); err != nil && os.IsNotExist(err) {
|
||||
log.Infof("intermediate stash directory %s doesn't exist, creating", IntermediateStash)
|
||||
err = os.MkdirAll(IntermediateStash, 0755)
|
||||
if err != nil {
|
||||
log.Errorf("failed to create intermediate stash directory %s: %v", IntermediateStash, err)
|
||||
return err
|
||||
}
|
||||
log.Infof("intermediate stash directory %s created", IntermediateStash)
|
||||
}
|
||||
}
|
||||
// stores URLs and certificate signatures that have been seen
|
||||
seen := map[string]bool{}
|
||||
var foundChains int
|
||||
|
||||
// Construct a verify chain as a reversed partial bundle,
|
||||
// such that the certs are ordered by promxity to the root CAs.
|
||||
var chain []*fetchedIntermediate
|
||||
for i, cert := range certs {
|
||||
var name string
|
||||
|
||||
// Only construct filenames for non-leaf intermediate certs
|
||||
// so they will be saved to disk if necessary.
|
||||
// Leaf cert gets a empty name and will be skipped.
|
||||
if i > 0 {
|
||||
name = constructCertFileName(cert)
|
||||
}
|
||||
|
||||
chain = append([]*fetchedIntermediate{{cert, name}}, chain...)
|
||||
seen[string(cert.Signature)] = true
|
||||
}
|
||||
|
||||
// Verify the chain and store valid intermediates in the chain.
|
||||
// If it doesn't verify, fetch the intermediates and extend the chain
|
||||
// in a DFS manner and verify each time we hit a root.
|
||||
for {
|
||||
if len(chain) == 0 {
|
||||
log.Debugf("search complete")
|
||||
if foundChains == 0 {
|
||||
return x509.UnknownAuthorityError{}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
current := chain[0]
|
||||
var advanced bool
|
||||
if b.verifyChain(chain) {
|
||||
foundChains++
|
||||
}
|
||||
log.Debugf("walk AIA issuers")
|
||||
for _, url := range current.Cert.IssuingCertificateURL {
|
||||
if seen[url] {
|
||||
log.Debugf("url %s has been seen", url)
|
||||
continue
|
||||
}
|
||||
crt, err := fetchRemoteCertificate(url)
|
||||
if err != nil {
|
||||
continue
|
||||
} else if seen[string(crt.Cert.Signature)] {
|
||||
log.Debugf("fetched certificate is known")
|
||||
continue
|
||||
}
|
||||
seen[url] = true
|
||||
seen[string(crt.Cert.Signature)] = true
|
||||
chain = append([]*fetchedIntermediate{crt}, chain...)
|
||||
advanced = true
|
||||
break
|
||||
}
|
||||
|
||||
if !advanced {
|
||||
log.Debugf("didn't advance, stepping back")
|
||||
chain = chain[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bundle takes an X509 certificate (already in the
|
||||
// Certificate structure), a private key as crypto.Signer in one of the appropriate
|
||||
// formats (i.e. *rsa.PrivateKey or *ecdsa.PrivateKey, or even a opaque key), using them to
|
||||
// build a certificate bundle.
|
||||
func (b *Bundler) Bundle(certs []*x509.Certificate, key crypto.Signer, flavor BundleFlavor) (*Bundle, error) {
|
||||
log.Infof("bundling certificate for %+v", certs[0].Subject)
|
||||
if len(certs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Detect reverse ordering of the cert chain.
|
||||
if len(certs) > 1 && !partialVerify(certs) {
|
||||
rcerts := reverse(certs)
|
||||
if partialVerify(rcerts) {
|
||||
certs = rcerts
|
||||
}
|
||||
}
|
||||
|
||||
var ok bool
|
||||
cert := certs[0]
|
||||
if key != nil {
|
||||
switch {
|
||||
case cert.PublicKeyAlgorithm == x509.RSA:
|
||||
|
||||
var rsaPublicKey *rsa.PublicKey
|
||||
if rsaPublicKey, ok = key.Public().(*rsa.PublicKey); !ok {
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
|
||||
}
|
||||
if cert.PublicKey.(*rsa.PublicKey).N.Cmp(rsaPublicKey.N) != 0 {
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
|
||||
}
|
||||
case cert.PublicKeyAlgorithm == x509.ECDSA:
|
||||
var ecdsaPublicKey *ecdsa.PublicKey
|
||||
if ecdsaPublicKey, ok = key.Public().(*ecdsa.PublicKey); !ok {
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
|
||||
}
|
||||
if cert.PublicKey.(*ecdsa.PublicKey).X.Cmp(ecdsaPublicKey.X) != 0 {
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.KeyMismatch)
|
||||
}
|
||||
default:
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case cert.PublicKeyAlgorithm == x509.RSA:
|
||||
case cert.PublicKeyAlgorithm == x509.ECDSA:
|
||||
default:
|
||||
return nil, errors.New(errors.PrivateKeyError, errors.NotRSAOrECC)
|
||||
}
|
||||
}
|
||||
|
||||
bundle := new(Bundle)
|
||||
bundle.Cert = cert
|
||||
bundle.Key = key
|
||||
bundle.Issuer = &cert.Issuer
|
||||
bundle.Subject = &cert.Subject
|
||||
|
||||
bundle.buildHostnames()
|
||||
|
||||
if flavor == Force {
|
||||
// force bundle checks the certificates
|
||||
// forms a verification chain.
|
||||
if !partialVerify(certs) {
|
||||
return nil,
|
||||
errors.Wrap(errors.CertificateError, errors.VerifyFailed,
|
||||
goerr.New("Unable to verify the certificate chain"))
|
||||
}
|
||||
bundle.Chain = certs
|
||||
} else {
|
||||
// disallow self-signed cert
|
||||
if cert.CheckSignatureFrom(cert) == nil {
|
||||
return nil, errors.New(errors.CertificateError, errors.SelfSigned)
|
||||
}
|
||||
|
||||
chains, err := cert.Verify(b.VerifyOptions())
|
||||
if err != nil {
|
||||
log.Debugf("verification failed: %v", err)
|
||||
// If the error was an unknown authority, try to fetch
|
||||
// the intermediate specified in the AIA and add it to
|
||||
// the intermediates bundle.
|
||||
if _, ok := err.(x509.UnknownAuthorityError); !ok {
|
||||
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||
}
|
||||
|
||||
log.Debugf("searching for intermediates via AIA issuer")
|
||||
searchErr := b.fetchIntermediates(certs)
|
||||
if searchErr != nil {
|
||||
log.Debugf("search failed: %v", searchErr)
|
||||
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||
}
|
||||
|
||||
log.Debugf("verifying new chain")
|
||||
chains, err = cert.Verify(b.VerifyOptions())
|
||||
if err != nil {
|
||||
log.Debugf("failed to verify chain: %v", err)
|
||||
return nil, errors.Wrap(errors.CertificateError, errors.VerifyFailed, err)
|
||||
}
|
||||
log.Debugf("verify ok")
|
||||
}
|
||||
var matchingChains [][]*x509.Certificate
|
||||
switch flavor {
|
||||
case Optimal:
|
||||
matchingChains = optimalChains(chains)
|
||||
case Ubiquitous:
|
||||
if len(ubiquity.Platforms) == 0 {
|
||||
log.Warning("No metadata, Ubiquitous falls back to Optimal.")
|
||||
}
|
||||
matchingChains = ubiquitousChains(chains)
|
||||
default:
|
||||
matchingChains = ubiquitousChains(chains)
|
||||
}
|
||||
|
||||
bundle.Chain = matchingChains[0]
|
||||
}
|
||||
|
||||
statusCode := int(errors.Success)
|
||||
var messages []string
|
||||
// Check if bundle is expiring.
|
||||
expiringCerts := checkExpiringCerts(bundle.Chain)
|
||||
if len(expiringCerts) > 0 {
|
||||
statusCode |= errors.BundleExpiringBit
|
||||
messages = append(messages, expirationWarning(expiringCerts))
|
||||
}
|
||||
// Check if bundle contains SHA2 certs.
|
||||
if ubiquity.ChainHashUbiquity(bundle.Chain) <= ubiquity.SHA2Ubiquity {
|
||||
statusCode |= errors.BundleNotUbiquitousBit
|
||||
messages = append(messages, sha2Warning)
|
||||
}
|
||||
// Check if bundle contains ECDSA signatures.
|
||||
if ubiquity.ChainKeyAlgoUbiquity(bundle.Chain) <= ubiquity.ECDSA256Ubiquity {
|
||||
statusCode |= errors.BundleNotUbiquitousBit
|
||||
messages = append(messages, ecdsaWarning)
|
||||
}
|
||||
|
||||
// when forcing a bundle, bundle ubiquity doesn't matter
|
||||
// also we don't retrieve the anchoring root of the bundle
|
||||
var untrusted []string
|
||||
if flavor != Force {
|
||||
// Add root store presence info
|
||||
root := bundle.Chain[len(bundle.Chain)-1]
|
||||
bundle.Root = root
|
||||
log.Infof("the anchoring root is %v", root.Subject)
|
||||
// Check if there is any platform that doesn't trust the chain.
|
||||
// Also, an warning will be generated if ubiquity.Platforms is nil,
|
||||
untrusted = ubiquity.UntrustedPlatforms(root)
|
||||
untrustedMsg := untrustedPlatformsWarning(untrusted)
|
||||
if len(untrustedMsg) > 0 {
|
||||
log.Debug("Populate untrusted platform warning.")
|
||||
statusCode |= errors.BundleNotUbiquitousBit
|
||||
messages = append(messages, untrustedMsg)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there is any platform that rejects the chain because of SHA1 deprecation.
|
||||
sha1Msgs := ubiquity.SHA1DeprecationMessages(bundle.Chain)
|
||||
if len(sha1Msgs) > 0 {
|
||||
log.Debug("Populate SHA1 deprecation warning.")
|
||||
statusCode |= errors.BundleNotUbiquitousBit
|
||||
messages = append(messages, sha1Msgs...)
|
||||
}
|
||||
|
||||
bundle.Status = &BundleStatus{ExpiringSKIs: getSKIs(bundle.Chain, expiringCerts), Code: statusCode, Messages: messages, Untrusted: untrusted}
|
||||
|
||||
// attempt to not to include the root certificate for optimization
|
||||
if flavor != Force {
|
||||
// Include at least one intermediate if the leaf has enabled OCSP and is not CA.
|
||||
if bundle.Cert.OCSPServer != nil && !bundle.Cert.IsCA && len(bundle.Chain) <= 2 {
|
||||
// No op. Return one intermediate if there is one.
|
||||
} else {
|
||||
// do not include the root.
|
||||
bundle.Chain = bundle.Chain[:len(bundle.Chain)-1]
|
||||
}
|
||||
}
|
||||
|
||||
bundle.Status.IsRebundled = diff(bundle.Chain, certs)
|
||||
bundle.Expires = helpers.ExpiryTime(bundle.Chain)
|
||||
bundle.LeafExpires = bundle.Chain[0].NotAfter
|
||||
|
||||
log.Debugf("bundle complete")
|
||||
return bundle, nil
|
||||
}
|
||||
|
||||
// checkExpiringCerts returns indices of certs that are expiring within 30 days.
|
||||
func checkExpiringCerts(chain []*x509.Certificate) (expiringIntermediates []int) {
|
||||
now := time.Now()
|
||||
for i, cert := range chain {
|
||||
if cert.NotAfter.Sub(now).Hours() < 720 {
|
||||
expiringIntermediates = append(expiringIntermediates, i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getSKIs returns a list of cert subject key id in the bundle chain with matched indices.
|
||||
func getSKIs(chain []*x509.Certificate, indices []int) (skis []string) {
|
||||
for _, index := range indices {
|
||||
ski := fmt.Sprintf("%X", chain[index].SubjectKeyId)
|
||||
skis = append(skis, ski)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// expirationWarning generates a warning message with expiring certs.
|
||||
func expirationWarning(expiringIntermediates []int) (ret string) {
|
||||
if len(expiringIntermediates) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ret = expiringWarningStub
|
||||
if len(expiringIntermediates) > 1 {
|
||||
ret = ret + "The expiring certs are"
|
||||
} else {
|
||||
ret = ret + "The expiring cert is"
|
||||
}
|
||||
for _, index := range expiringIntermediates {
|
||||
ret = ret + " #" + strconv.Itoa(index+1)
|
||||
}
|
||||
ret = ret + " in the chain."
|
||||
return
|
||||
}
|
||||
|
||||
// untrustedPlatformsWarning generates a warning message with untrusted platform names.
|
||||
func untrustedPlatformsWarning(platforms []string) string {
|
||||
if len(ubiquity.Platforms) == 0 {
|
||||
return ubiquityWarning
|
||||
}
|
||||
|
||||
if len(platforms) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
msg := untrustedWarningStub
|
||||
for i, platform := range platforms {
|
||||
if i > 0 {
|
||||
msg += ","
|
||||
}
|
||||
msg += " " + platform
|
||||
}
|
||||
msg += "."
|
||||
return msg
|
||||
}
|
||||
|
||||
// Optimal chains are the shortest chains, with newest intermediates and most advanced crypto suite being the tie breaker.
|
||||
func optimalChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
|
||||
// Find shortest chains
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
|
||||
// Find the chains with longest expiry.
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainExpiry)
|
||||
// Find the chains with more advanced crypto suite
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainCryptoSuite)
|
||||
|
||||
return chains
|
||||
}
|
||||
|
||||
// Ubiquitous chains are the chains with highest platform coverage and break ties with the optimal strategy.
|
||||
func ubiquitousChains(chains [][]*x509.Certificate) [][]*x509.Certificate {
|
||||
// Filter out chains with highest cross platform ubiquity.
|
||||
chains = ubiquity.Filter(chains, ubiquity.ComparePlatformUbiquity)
|
||||
// Prefer that all intermediates are SHA-2 certs if the leaf is a SHA-2 cert, in order to improve ubiquity.
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareSHA2Homogeneity)
|
||||
// Filter shortest chains
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainLength)
|
||||
// Filter chains with highest signature hash ubiquity.
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainHashUbiquity)
|
||||
// Filter chains with highest keyAlgo ubiquity.
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareChainKeyAlgoUbiquity)
|
||||
// Filter chains with intermediates that last longer.
|
||||
chains = ubiquity.Filter(chains, ubiquity.CompareExpiryUbiquity)
|
||||
// Use the optimal strategy as final tie breaker.
|
||||
return optimalChains(chains)
|
||||
}
|
||||
|
||||
// diff checkes if two input cert chains are not identical
|
||||
func diff(chain1, chain2 []*x509.Certificate) bool {
|
||||
// Check if bundled one is different from the input.
|
||||
diff := false
|
||||
if len(chain1) != len(chain2) {
|
||||
diff = true
|
||||
} else {
|
||||
for i := 0; i < len(chain1); i++ {
|
||||
cert1 := chain1[i]
|
||||
cert2 := chain2[i]
|
||||
// Use signature to differentiate.
|
||||
if !bytes.Equal(cert1.Signature, cert2.Signature) {
|
||||
diff = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return diff
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
// 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"],
|
||||
)
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,57 +0,0 @@
|
|||
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)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,352 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
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"],
|
||||
)
|
|
@ -1,166 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cli.go",
|
||||
"config.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli",
|
||||
importpath = "github.com/cloudflare/cfssl/cli",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/config: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/signer/universal: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/cli/bundle:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/certinfo:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/crl:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/gencert:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/gencrl:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/gencsr:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/genkey:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/info:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/ocspdump:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/ocsprefresh:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/ocspserve:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/ocspsign:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/printdefault:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/revoke:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/scan:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/selfsign:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/serve:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/sign:all-srcs",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/version:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
|
@ -1,28 +0,0 @@
|
|||
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/cli/bundle",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/bundle",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/bundler:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,88 +0,0 @@
|
|||
// Package bundle implements the bundle command.
|
||||
package bundle
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/cfssl/bundler"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/ubiquity"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl bundle'
|
||||
var bundlerUsageText = `cfssl bundle -- create a certificate bundle that contains the client cert
|
||||
|
||||
Usage of bundle:
|
||||
- Bundle local certificate files
|
||||
cfssl bundle -cert file [-ca-bundle file] [-int-bundle file] [-int-dir dir] [-metadata file] [-key keyfile] [-flavor optimal|ubiquitous|force] [-password password]
|
||||
- Bundle certificate from remote server.
|
||||
cfssl bundle -domain domain_name [-ip ip_address] [-ca-bundle file] [-int-bundle file] [-int-dir dir] [-metadata file]
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// flags used by 'cfssl bundle'
|
||||
var bundlerFlags = []string{"cert", "key", "ca-bundle", "int-bundle", "flavor", "int-dir", "metadata", "domain", "ip", "password"}
|
||||
|
||||
// bundlerMain is the main CLI of bundler functionality.
|
||||
func bundlerMain(args []string, c cli.Config) (err error) {
|
||||
bundler.IntermediateStash = c.IntDir
|
||||
ubiquity.LoadPlatforms(c.Metadata)
|
||||
flavor := bundler.BundleFlavor(c.Flavor)
|
||||
var b *bundler.Bundler
|
||||
// If it is a force bundle, don't require ca bundle and intermediate bundle
|
||||
// Otherwise, initialize a bundler with CA bundle and intermediate bundle.
|
||||
if flavor == bundler.Force {
|
||||
b = &bundler.Bundler{}
|
||||
} else {
|
||||
b, err = bundler.NewBundler(c.CABundleFile, c.IntBundleFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var bundle *bundler.Bundle
|
||||
if c.CertFile != "" {
|
||||
if c.CertFile == "-" {
|
||||
var certPEM, keyPEM []byte
|
||||
certPEM, err = cli.ReadStdin(c.CertFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if c.KeyFile != "" {
|
||||
keyPEM, err = cli.ReadStdin(c.KeyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
bundle, err = b.BundleFromPEMorDER(certPEM, keyPEM, flavor, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Bundle the client cert
|
||||
bundle, err = b.BundleFromFile(c.CertFile, c.KeyFile, flavor, c.Password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if c.Domain != "" {
|
||||
bundle, err = b.BundleFromRemote(c.Domain, c.IP, flavor)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return errors.New("Must specify bundle target through -cert or -domain")
|
||||
}
|
||||
|
||||
marshaled, err := bundle.MarshalJSON()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s", marshaled)
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'bundle'
|
||||
var Command = &cli.Command{UsageText: bundlerUsageText, Flags: bundlerFlags, Main: bundlerMain}
|
|
@ -1,27 +0,0 @@
|
|||
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/cli/certinfo",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/certinfo",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/certinfo:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,89 +0,0 @@
|
|||
// Package certinfo implements the certinfo command
|
||||
package certinfo
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/cfssl/certinfo"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl certinfo'
|
||||
var dataUsageText = `cfssl certinfo -- output certinfo about the given cert
|
||||
|
||||
Usage of certinfo:
|
||||
- Data from local certificate files
|
||||
cfssl certinfo -cert file
|
||||
- Data from local CSR file
|
||||
cfssl certinfo -csr file
|
||||
- Data from certificate from remote server.
|
||||
cfssl certinfo -domain domain_name
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// flags used by 'cfssl certinfo'
|
||||
var certinfoFlags = []string{"cert", "csr", "domain"}
|
||||
|
||||
// certinfoMain is the main CLI of certinfo functionality
|
||||
func certinfoMain(args []string, c cli.Config) (err error) {
|
||||
var cert *certinfo.Certificate
|
||||
var csr *x509.CertificateRequest
|
||||
|
||||
if c.CertFile != "" {
|
||||
if c.CertFile == "-" {
|
||||
var certPEM []byte
|
||||
if certPEM, err = cli.ReadStdin(c.CertFile); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cert, err = certinfo.ParseCertificatePEM(certPEM); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if cert, err = certinfo.ParseCertificateFile(c.CertFile); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if c.CSRFile != "" {
|
||||
if c.CSRFile == "-" {
|
||||
var csrPEM []byte
|
||||
if csrPEM, err = cli.ReadStdin(c.CSRFile); err != nil {
|
||||
return
|
||||
}
|
||||
if csr, err = certinfo.ParseCSRPEM(csrPEM); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if csr, err = certinfo.ParseCSRFile(c.CSRFile); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if c.Domain != "" {
|
||||
if cert, err = certinfo.ParseCertificateDomain(c.Domain); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return errors.New("Must specify certinfo target through -cert, -csr, or -domain")
|
||||
}
|
||||
|
||||
var b []byte
|
||||
if cert != nil {
|
||||
b, err = json.MarshalIndent(cert, "", " ")
|
||||
} else if csr != nil {
|
||||
b, err = json.MarshalIndent(csr, "", " ")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(b))
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'certinfo'
|
||||
var Command = &cli.Command{UsageText: dataUsageText, Flags: certinfoFlags, Main: certinfoMain}
|
|
@ -1,199 +0,0 @@
|
|||
// Package cli provides the template for adding new cfssl commands
|
||||
package cli
|
||||
|
||||
/*
|
||||
cfssl is the command line tool to issue/sign/bundle client certificate. It's
|
||||
also a tool to start a HTTP server to handle web requests for signing, bundling
|
||||
and verification.
|
||||
|
||||
Usage:
|
||||
cfssl command [-flags] arguments
|
||||
|
||||
The commands are defined in the cli subpackages and include
|
||||
|
||||
bundle create a certificate bundle
|
||||
sign signs a certificate signing request (CSR)
|
||||
serve starts a HTTP server handling sign and bundle requests
|
||||
version prints the current cfssl version
|
||||
genkey generates a key and an associated CSR
|
||||
gencert generates a key and a signed certificate
|
||||
gencsr generates a certificate request
|
||||
selfsign generates a self-signed certificate
|
||||
ocspsign signs an OCSP response
|
||||
|
||||
Use "cfssl [command] -help" to find out more about a command.
|
||||
*/
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/cloudflare/cfssl/config"
|
||||
)
|
||||
|
||||
// Command holds the implementation details of a cfssl command.
|
||||
type Command struct {
|
||||
// The Usage Text
|
||||
UsageText string
|
||||
// Flags to look up in the global table
|
||||
Flags []string
|
||||
// Main runs the command, args are the arguments after flags
|
||||
Main func(args []string, c Config) error
|
||||
}
|
||||
|
||||
var cmdName string
|
||||
|
||||
// usage is the cfssl usage heading. It will be appended with names of defined commands in cmds
|
||||
// to form the final usage message of cfssl.
|
||||
const usage = `Usage:
|
||||
Available commands:
|
||||
`
|
||||
|
||||
// printDefaultValue is a helper function to print out a user friendly
|
||||
// usage message of a flag. It's useful since we want to write customized
|
||||
// usage message on selected subsets of the global flag set. It is
|
||||
// borrowed from standard library source code. Since flag value type is
|
||||
// not exported, default string flag values are printed without
|
||||
// quotes. The only exception is the empty string, which is printed as "".
|
||||
func printDefaultValue(f *flag.Flag) {
|
||||
format := " -%s=%s: %s\n"
|
||||
if f.DefValue == "" {
|
||||
format = " -%s=%q: %s\n"
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage)
|
||||
}
|
||||
|
||||
// PopFirstArgument returns the first element and the rest of a string
|
||||
// slice and return error if failed to do so. It is a helper function
|
||||
// to parse non-flag arguments previously used in cfssl commands.
|
||||
func PopFirstArgument(args []string) (string, []string, error) {
|
||||
if len(args) < 1 {
|
||||
return "", nil, errors.New("not enough arguments are supplied --- please refer to the usage")
|
||||
}
|
||||
return args[0], args[1:], nil
|
||||
}
|
||||
|
||||
// Start is the entrance point of cfssl command line tools.
|
||||
func Start(cmds map[string]*Command) error {
|
||||
// cfsslFlagSet is the flag sets for cfssl.
|
||||
var cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ExitOnError)
|
||||
var c Config
|
||||
|
||||
registerFlags(&c, cfsslFlagSet)
|
||||
// Initial parse of command line arguments. By convention, only -h/-help is supported.
|
||||
if flag.Usage == nil {
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, usage)
|
||||
for name := range cmds {
|
||||
fmt.Fprintf(os.Stderr, "\t%s\n", name)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Top-level flags:\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
fmt.Fprintf(os.Stderr, "No command is given.\n")
|
||||
flag.Usage()
|
||||
return errors.New("no command was given")
|
||||
}
|
||||
|
||||
// Clip out the command name and args for the command
|
||||
cmdName = flag.Arg(0)
|
||||
args := flag.Args()[1:]
|
||||
cmd, found := cmds[cmdName]
|
||||
if !found {
|
||||
fmt.Fprintf(os.Stderr, "Command %s is not defined.\n", cmdName)
|
||||
flag.Usage()
|
||||
return errors.New("undefined command")
|
||||
}
|
||||
// always have flag 'loglevel' for each command
|
||||
cmd.Flags = append(cmd.Flags, "loglevel")
|
||||
// The usage of each individual command is re-written to mention
|
||||
// flags defined and referenced only in that command.
|
||||
cfsslFlagSet.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "\t%s", cmd.UsageText)
|
||||
for _, name := range cmd.Flags {
|
||||
if f := cfsslFlagSet.Lookup(name); f != nil {
|
||||
printDefaultValue(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse all flags and take the rest as argument lists for the command
|
||||
cfsslFlagSet.Parse(args)
|
||||
args = cfsslFlagSet.Args()
|
||||
|
||||
var err error
|
||||
if c.ConfigFile != "" {
|
||||
c.CFG, err = config.LoadFile(c.ConfigFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to load config file: %v", err)
|
||||
return errors.New("failed to load config file")
|
||||
}
|
||||
}
|
||||
|
||||
if err := cmd.Main(args, c); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadStdin reads from stdin if the file is "-"
|
||||
func ReadStdin(filename string) ([]byte, error) {
|
||||
if filename == "-" {
|
||||
return ioutil.ReadAll(os.Stdin)
|
||||
}
|
||||
return ioutil.ReadFile(filename)
|
||||
}
|
||||
|
||||
// PrintCert outputs a cert, key and csr to stdout
|
||||
func PrintCert(key, csrBytes, cert []byte) {
|
||||
out := map[string]string{}
|
||||
if cert != nil {
|
||||
out["cert"] = string(cert)
|
||||
}
|
||||
|
||||
if key != nil {
|
||||
out["key"] = string(key)
|
||||
}
|
||||
|
||||
if csrBytes != nil {
|
||||
out["csr"] = string(csrBytes)
|
||||
}
|
||||
|
||||
jsonOut, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", jsonOut)
|
||||
}
|
||||
|
||||
// PrintOCSPResponse outputs an OCSP response to stdout
|
||||
// ocspResponse is base64 encoded
|
||||
func PrintOCSPResponse(resp []byte) {
|
||||
b64Resp := base64.StdEncoding.EncodeToString(resp)
|
||||
|
||||
out := map[string]string{"ocspResponse": b64Resp}
|
||||
jsonOut, err := json.Marshal(out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Printf("%s\n", jsonOut)
|
||||
}
|
||||
|
||||
// PrintCRL outputs the CRL to stdout
|
||||
func PrintCRL(certList []byte) {
|
||||
b64Resp := base64.StdEncoding.EncodeToString(certList)
|
||||
|
||||
fmt.Printf("%s\n", b64Resp)
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/config"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/signer/universal"
|
||||
)
|
||||
|
||||
// Config is a type to hold flag values used by cfssl commands.
|
||||
type Config struct {
|
||||
Hostname string
|
||||
CertFile string
|
||||
CSRFile string
|
||||
CAFile string
|
||||
CAKeyFile string
|
||||
TLSCertFile string
|
||||
TLSKeyFile string
|
||||
MutualTLSCAFile string
|
||||
MutualTLSCNRegex string
|
||||
TLSRemoteCAs string
|
||||
MutualTLSCertFile string
|
||||
MutualTLSKeyFile string
|
||||
KeyFile string
|
||||
IntermediatesFile string
|
||||
CABundleFile string
|
||||
IntBundleFile string
|
||||
Address string
|
||||
Port int
|
||||
MinTLSVersion string
|
||||
Password string
|
||||
ConfigFile string
|
||||
CFG *config.Config
|
||||
Profile string
|
||||
IsCA bool
|
||||
RenewCA bool
|
||||
IntDir string
|
||||
Flavor string
|
||||
Metadata string
|
||||
Domain string
|
||||
IP string
|
||||
Remote string
|
||||
Label string
|
||||
AuthKey string
|
||||
ResponderFile string
|
||||
ResponderKeyFile string
|
||||
Status string
|
||||
Reason string
|
||||
RevokedAt string
|
||||
Interval time.Duration
|
||||
List bool
|
||||
Family string
|
||||
Timeout time.Duration
|
||||
Scanner string
|
||||
CSVFile string
|
||||
NumWorkers int
|
||||
MaxHosts int
|
||||
Responses string
|
||||
Path string
|
||||
CRL string
|
||||
Usage string
|
||||
PGPPrivate string
|
||||
PGPName string
|
||||
Serial string
|
||||
CNOverride string
|
||||
AKI string
|
||||
DBConfigFile string
|
||||
CRLExpiration time.Duration
|
||||
Disable string
|
||||
}
|
||||
|
||||
// registerFlags defines all cfssl command flags and associates their values with variables.
|
||||
func registerFlags(c *Config, f *flag.FlagSet) {
|
||||
f.StringVar(&c.Hostname, "hostname", "", "Hostname for the cert, could be a comma-separated hostname list")
|
||||
f.StringVar(&c.CertFile, "cert", "", "Client certificate that contains the public key")
|
||||
f.StringVar(&c.CSRFile, "csr", "", "Certificate signature request file for new public key")
|
||||
f.StringVar(&c.CAFile, "ca", "", "CA used to sign the new certificate -- accepts '[file:]fname' or 'env:varname'")
|
||||
f.StringVar(&c.CAKeyFile, "ca-key", "", "CA private key -- accepts '[file:]fname' or 'env:varname'")
|
||||
f.StringVar(&c.TLSCertFile, "tls-cert", "", "Other endpoint CA to set up TLS protocol")
|
||||
f.StringVar(&c.TLSKeyFile, "tls-key", "", "Other endpoint CA private key")
|
||||
f.StringVar(&c.MutualTLSCAFile, "mutual-tls-ca", "", "Mutual TLS - require clients be signed by this CA ")
|
||||
f.StringVar(&c.MutualTLSCNRegex, "mutual-tls-cn", "", "Mutual TLS - regex for whitelist of allowed client CNs")
|
||||
f.StringVar(&c.TLSRemoteCAs, "tls-remote-ca", "", "CAs to trust for remote TLS requests")
|
||||
f.StringVar(&c.MutualTLSCertFile, "mutual-tls-client-cert", "", "Mutual TLS - client certificate to call remote instance requiring client certs")
|
||||
f.StringVar(&c.MutualTLSKeyFile, "mutual-tls-client-key", "", "Mutual TLS - client key to call remote instance requiring client certs")
|
||||
f.StringVar(&c.KeyFile, "key", "", "private key for the certificate")
|
||||
f.StringVar(&c.IntermediatesFile, "intermediates", "", "intermediate certs")
|
||||
f.StringVar(&c.CABundleFile, "ca-bundle", "", "path to root certificate store")
|
||||
f.StringVar(&c.IntBundleFile, "int-bundle", "", "path to intermediate certificate store")
|
||||
f.StringVar(&c.Address, "address", "127.0.0.1", "Address to bind")
|
||||
f.IntVar(&c.Port, "port", 8888, "Port to bind")
|
||||
f.StringVar(&c.MinTLSVersion, "min-tls-version", "", "Minimum version of TLS to use, defaults to 1.0")
|
||||
f.StringVar(&c.ConfigFile, "config", "", "path to configuration file")
|
||||
f.StringVar(&c.Profile, "profile", "", "signing profile to use")
|
||||
f.BoolVar(&c.IsCA, "initca", false, "initialise new CA")
|
||||
f.BoolVar(&c.RenewCA, "renewca", false, "re-generate a CA certificate from existing CA certificate/key")
|
||||
f.StringVar(&c.IntDir, "int-dir", "", "specify intermediates directory")
|
||||
f.StringVar(&c.Flavor, "flavor", "ubiquitous", "Bundle Flavor: ubiquitous, optimal and force.")
|
||||
f.StringVar(&c.Metadata, "metadata", "", "Metadata file for root certificate presence. The content of the file is a json dictionary (k,v): each key k is SHA-1 digest of a root certificate while value v is a list of key store filenames.")
|
||||
f.StringVar(&c.Domain, "domain", "", "remote server domain name")
|
||||
f.StringVar(&c.IP, "ip", "", "remote server ip")
|
||||
f.StringVar(&c.Remote, "remote", "", "remote CFSSL server")
|
||||
f.StringVar(&c.Label, "label", "", "key label to use in remote CFSSL server")
|
||||
f.StringVar(&c.AuthKey, "authkey", "", "key to authenticate requests to remote CFSSL server")
|
||||
f.StringVar(&c.ResponderFile, "responder", "", "Certificate for OCSP responder")
|
||||
f.StringVar(&c.ResponderKeyFile, "responder-key", "", "private key for OCSP responder certificate")
|
||||
f.StringVar(&c.Status, "status", "good", "Status of the certificate: good, revoked, unknown")
|
||||
f.StringVar(&c.Reason, "reason", "0", "Reason code for revocation")
|
||||
f.StringVar(&c.RevokedAt, "revoked-at", "now", "Date of revocation (YYYY-MM-DD)")
|
||||
f.DurationVar(&c.Interval, "interval", 4*helpers.OneDay, "Interval between OCSP updates (default: 96h)")
|
||||
f.BoolVar(&c.List, "list", false, "list possible scanners")
|
||||
f.StringVar(&c.Family, "family", "", "scanner family regular expression")
|
||||
f.StringVar(&c.Scanner, "scanner", "", "scanner regular expression")
|
||||
f.DurationVar(&c.Timeout, "timeout", 5*time.Minute, "duration (ns, us, ms, s, m, h) to scan each host before timing out")
|
||||
f.StringVar(&c.CSVFile, "csv", "", "file containing CSV of hosts")
|
||||
f.IntVar(&c.NumWorkers, "num-workers", 10, "number of workers to use for scan")
|
||||
f.IntVar(&c.MaxHosts, "max-hosts", 100, "maximum number of hosts to scan")
|
||||
f.StringVar(&c.Responses, "responses", "", "file to load OCSP responses from")
|
||||
f.StringVar(&c.Path, "path", "/", "Path on which the server will listen")
|
||||
f.StringVar(&c.CRL, "crl", "", "CRL URL Override")
|
||||
f.StringVar(&c.Password, "password", "0", "Password for accessing PKCS #12 data passed to bundler")
|
||||
f.StringVar(&c.Usage, "usage", "", "usage of private key")
|
||||
f.StringVar(&c.PGPPrivate, "pgp-private", "", "file to load a PGP Private key decryption")
|
||||
f.StringVar(&c.PGPName, "pgp-name", "", "PGP public key name, can be a comma-sepearted key name list")
|
||||
f.StringVar(&c.Serial, "serial", "", "certificate serial number")
|
||||
f.StringVar(&c.CNOverride, "cn", "", "certificate common name (CN)")
|
||||
f.StringVar(&c.AKI, "aki", "", "certificate issuer (authority) key identifier")
|
||||
f.StringVar(&c.DBConfigFile, "db-config", "", "certificate db configuration file")
|
||||
f.DurationVar(&c.CRLExpiration, "expiry", 7*helpers.OneDay, "time from now after which the CRL will expire (default: one week)")
|
||||
f.IntVar(&log.Level, "loglevel", log.LevelInfo, "Log level (0 = DEBUG, 5 = FATAL)")
|
||||
f.StringVar(&c.Disable, "disable", "", "endpoints to disable")
|
||||
}
|
||||
|
||||
// RootFromConfig returns a universal signer Root structure that can
|
||||
// be used to produce a signer.
|
||||
func RootFromConfig(c *Config) universal.Root {
|
||||
return universal.Root{
|
||||
Config: map[string]string{
|
||||
"cert-file": c.CAFile,
|
||||
"key-file": c.CAKeyFile,
|
||||
},
|
||||
ForceRemote: c.Remote != "",
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
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/cli/crl",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/crl",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/dbconf:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/sql:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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",
|
||||
"//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"],
|
||||
)
|
|
@ -1,105 +0,0 @@
|
|||
//Package crl implements the crl command
|
||||
package crl
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/cloudflare/cfssl/certdb/dbconf"
|
||||
certsql "github.com/cloudflare/cfssl/certdb/sql"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/crl"
|
||||
cferr "github.com/cloudflare/cfssl/errors"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var crlUsageText = `cfssl crl -- generate a new Certificate Revocation List from Database
|
||||
|
||||
Usage of crl:
|
||||
cfssl crl
|
||||
|
||||
Flags:
|
||||
`
|
||||
var crlFlags = []string{"db-config", "ca", "ca-key", "expiry"}
|
||||
|
||||
func generateCRL(c cli.Config) (crlBytes []byte, err error) {
|
||||
if c.CAFile == "" {
|
||||
log.Error("need CA certificate (provide one with -ca)")
|
||||
return
|
||||
}
|
||||
|
||||
if c.CAKeyFile == "" {
|
||||
log.Error("need CA key (provide one with -ca-key)")
|
||||
return
|
||||
}
|
||||
|
||||
var db *sqlx.DB
|
||||
if c.DBConfigFile != "" {
|
||||
db, err = dbconf.DBFromConfig(c.DBConfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
log.Error("no Database specified!")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbAccessor := certsql.NewAccessor(db)
|
||||
|
||||
log.Debug("loading CA: ", c.CAFile)
|
||||
ca, err := helpers.ReadBytes(c.CAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debug("loading CA key: ", c.CAKeyFile)
|
||||
cakey, err := helpers.ReadBytes(c.CAKeyFile)
|
||||
if err != nil {
|
||||
return nil, cferr.Wrap(cferr.CertificateError, cferr.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
|
||||
}
|
||||
|
||||
certs, err := dbAccessor.GetRevokedAndUnexpiredCertificates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := crl.NewCRLFromDB(certs, issuerCert, key, c.CRLExpiration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func crlMain(args []string, c cli.Config) (err error) {
|
||||
req, err := generateCRL(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli.PrintCRL(req)
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'crl'
|
||||
var Command = &cli.Command{UsageText: crlUsageText, Flags: crlFlags, Main: crlMain}
|
|
@ -1,33 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["gencert.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/gencert",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/gencert",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/api/generator:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/genkey:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/sign:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/initca: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"],
|
||||
)
|
|
@ -1,165 +0,0 @@
|
|||
// Package gencert implements the gencert command.
|
||||
package gencert
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/cloudflare/cfssl/api/generator"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/cli/genkey"
|
||||
"github.com/cloudflare/cfssl/cli/sign"
|
||||
"github.com/cloudflare/cfssl/csr"
|
||||
"github.com/cloudflare/cfssl/initca"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/signer"
|
||||
)
|
||||
|
||||
var gencertUsageText = `cfssl gencert -- generate a new key and signed certificate
|
||||
|
||||
Usage of gencert:
|
||||
Generate a new key and cert from CSR:
|
||||
cfssl gencert -initca CSRJSON
|
||||
cfssl gencert -ca cert -ca-key key [-config config] [-profile profile] [-hostname hostname] CSRJSON
|
||||
cfssl gencert -remote remote_host [-config config] [-profile profile] [-label label] [-hostname hostname] CSRJSON
|
||||
|
||||
Re-generate a CA cert with the CA key and CSR:
|
||||
cfssl gencert -initca -ca-key key CSRJSON
|
||||
|
||||
Re-generate a CA cert with the CA key and certificate:
|
||||
cfssl gencert -renewca -ca cert -ca-key key
|
||||
|
||||
Arguments:
|
||||
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var gencertFlags = []string{"initca", "remote", "ca", "ca-key", "config", "cn", "hostname", "profile", "label"}
|
||||
|
||||
func gencertMain(args []string, c cli.Config) error {
|
||||
if c.RenewCA {
|
||||
log.Infof("re-generate a CA certificate from CA cert and key")
|
||||
cert, err := initca.RenewFromPEM(c.CAFile, c.CAKeyFile)
|
||||
if err != nil {
|
||||
log.Errorf("%v\n", err)
|
||||
return err
|
||||
}
|
||||
cli.PrintCert(nil, nil, cert)
|
||||
return nil
|
||||
}
|
||||
|
||||
csrJSONFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
return errors.New("only one argument is accepted, please check with usage")
|
||||
}
|
||||
|
||||
csrJSONFileBytes, err := cli.ReadStdin(csrJSONFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := csr.CertificateRequest{
|
||||
KeyRequest: csr.NewBasicKeyRequest(),
|
||||
}
|
||||
err = json.Unmarshal(csrJSONFileBytes, &req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.CNOverride != "" {
|
||||
req.CN = c.CNOverride
|
||||
}
|
||||
switch {
|
||||
case c.IsCA:
|
||||
var key, csrPEM, cert []byte
|
||||
if c.CAKeyFile != "" {
|
||||
log.Infof("re-generate a CA certificate from CSR and CA key")
|
||||
cert, csrPEM, err = initca.NewFromPEM(&req, c.CAKeyFile)
|
||||
if err != nil {
|
||||
log.Errorf("%v\n", err)
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Infof("generating a new CA key and certificate from CSR")
|
||||
cert, csrPEM, key, err = initca.New(&req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
cli.PrintCert(key, csrPEM, cert)
|
||||
|
||||
default:
|
||||
if req.CA != nil {
|
||||
err = errors.New("ca section only permitted in initca")
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Hostname != "" {
|
||||
req.Hosts = signer.SplitHosts(c.Hostname)
|
||||
}
|
||||
// Remote can be forced on the command line or in the config
|
||||
if c.Remote == "" && c.CFG == nil {
|
||||
if c.CAFile == "" {
|
||||
log.Error("need a CA certificate (provide one with -ca)")
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.CAKeyFile == "" {
|
||||
log.Error("need a CA key (provide one with -ca-key)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var key, csrBytes []byte
|
||||
g := &csr.Generator{Validator: genkey.Validator}
|
||||
csrBytes, key, err = g.ProcessRequest(&req)
|
||||
if err != nil {
|
||||
key = nil
|
||||
return err
|
||||
}
|
||||
|
||||
s, err := sign.SignerFromConfig(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var cert []byte
|
||||
signReq := signer.SignRequest{
|
||||
Request: string(csrBytes),
|
||||
Hosts: signer.SplitHosts(c.Hostname),
|
||||
Profile: c.Profile,
|
||||
Label: c.Label,
|
||||
}
|
||||
|
||||
if c.CRL != "" {
|
||||
signReq.CRLOverride = c.CRL
|
||||
}
|
||||
cert, err = s.Sign(signReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This follows 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"), states:
|
||||
//
|
||||
// "Applicant information MUST include, but not be limited to, at least one
|
||||
// Fully-Qualified Domain Name or IP address to be included in the Certificate’s
|
||||
// SubjectAltName extension."
|
||||
if len(signReq.Hosts) == 0 && len(req.Hosts) == 0 {
|
||||
log.Warning(generator.CSRNoHostMessage)
|
||||
}
|
||||
|
||||
cli.PrintCert(key, csrBytes, cert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'gencert'
|
||||
var Command = &cli.Command{UsageText: gencertUsageText, Flags: gencertFlags, Main: gencertMain}
|
|
@ -1,27 +0,0 @@
|
|||
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/cli/gencrl",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/gencrl",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/crl: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"],
|
||||
)
|
|
@ -1,82 +0,0 @@
|
|||
//Package gencrl implements the gencrl command
|
||||
package gencrl
|
||||
|
||||
import (
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/crl"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var gencrlUsageText = `cfssl gencrl -- generate a new Certificate Revocation List
|
||||
|
||||
Usage of gencrl:
|
||||
cfssl gencrl INPUTFILE CERT KEY TIME
|
||||
|
||||
Arguments:
|
||||
INPUTFILE: Text file with one serial number per line, use '-' for reading text from stdin
|
||||
CERT: The certificate that is signing this CRL, use '-' for reading text from stdin
|
||||
KEY: The private key of the certificate that is signing the CRL, use '-' for reading text from stdin
|
||||
TIME (OPTIONAL): The desired expiration from now, in seconds
|
||||
|
||||
Flags:
|
||||
`
|
||||
var gencrlFlags = []string{}
|
||||
|
||||
func gencrlMain(args []string, c cli.Config) (err error) {
|
||||
serialList, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
serialListBytes, err := cli.ReadStdin(serialList)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
certFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
certFileBytes, err := cli.ReadStdin(certFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keyFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
keyBytes, err := cli.ReadStdin(keyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Default value if no expiry time is given
|
||||
timeString := string("0")
|
||||
|
||||
if len(args) > 0 {
|
||||
timeArg, _, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
timeString = string(timeArg)
|
||||
|
||||
// This is used to get rid of newlines
|
||||
timeString = strings.TrimSpace(timeString)
|
||||
|
||||
}
|
||||
|
||||
req, err := crl.NewCRLFromFile(serialListBytes, certFileBytes, keyBytes, timeString)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cli.PrintCRL(req)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'gencrl'
|
||||
var Command = &cli.Command{UsageText: gencrlUsageText, Flags: gencrlFlags, Main: gencrlMain}
|
|
@ -1,29 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["gencsr.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/gencsr",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/gencsr",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/helpers: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"],
|
||||
)
|
|
@ -1,99 +0,0 @@
|
|||
// Package gencsr implements the gencsr command.
|
||||
package gencsr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/csr"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/signer"
|
||||
)
|
||||
|
||||
var gencsrUsageText = `cfssl gencsr -- generate a csr from a private key with existing CSR json specification or certificate
|
||||
|
||||
Usage of genkey:
|
||||
cfssl gencsr -key private_key_file [-host hostname_override] CSRJSON
|
||||
cfssl gencsr -key private_key_file [-host hostname_override] -cert certificate_file
|
||||
|
||||
Arguments:
|
||||
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var gencsrFlags = []string{"key", "cert"}
|
||||
|
||||
func gencsrMain(args []string, c cli.Config) (err error) {
|
||||
if c.KeyFile == "" {
|
||||
return errors.New("private key file is required through '-key', please check with usage")
|
||||
}
|
||||
|
||||
keyBytes, err := helpers.ReadBytes(c.KeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := helpers.ParsePrivateKeyPEM(keyBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// prepare a stub CertificateRequest
|
||||
req := &csr.CertificateRequest{
|
||||
KeyRequest: csr.NewBasicKeyRequest(),
|
||||
}
|
||||
|
||||
if c.CertFile != "" {
|
||||
if len(args) > 0 {
|
||||
return errors.New("no argument is accepted with '-cert', please check with usage")
|
||||
}
|
||||
|
||||
certBytes, err := helpers.ReadBytes(c.CertFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cert, err := helpers.ParseCertificatePEM(certBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req = csr.ExtractCertificateRequest(cert)
|
||||
} else {
|
||||
csrFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
return errors.New("only one argument is accepted, please check with usage")
|
||||
}
|
||||
|
||||
csrFileBytes, err := cli.ReadStdin(csrFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(csrFileBytes, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Hostname != "" {
|
||||
req.Hosts = signer.SplitHosts(c.Hostname)
|
||||
}
|
||||
|
||||
csrBytes, err := csr.Generate(key, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cli.PrintCert(keyBytes, csrBytes, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'gencsr'
|
||||
var Command = &cli.Command{UsageText: gencsrUsageText, Flags: gencsrFlags, Main: gencsrMain}
|
|
@ -1,28 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["genkey.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/genkey",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/genkey",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/initca: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"],
|
||||
)
|
|
@ -1,82 +0,0 @@
|
|||
// Package genkey implements the genkey command.
|
||||
package genkey
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/csr"
|
||||
"github.com/cloudflare/cfssl/initca"
|
||||
)
|
||||
|
||||
var genkeyUsageText = `cfssl genkey -- generate a new key and CSR
|
||||
|
||||
Usage of genkey:
|
||||
cfssl genkey CSRJSON
|
||||
|
||||
Arguments:
|
||||
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var genkeyFlags = []string{"initca", "config"}
|
||||
|
||||
func genkeyMain(args []string, c cli.Config) (err error) {
|
||||
csrFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(args) > 0 {
|
||||
return errors.New("only one argument is accepted, please check with usage")
|
||||
}
|
||||
|
||||
csrFileBytes, err := cli.ReadStdin(csrFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req := csr.CertificateRequest{
|
||||
KeyRequest: csr.NewBasicKeyRequest(),
|
||||
}
|
||||
err = json.Unmarshal(csrFileBytes, &req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.IsCA {
|
||||
var key, csrPEM, cert []byte
|
||||
cert, csrPEM, key, err = initca.New(&req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
cli.PrintCert(key, csrPEM, cert)
|
||||
} else {
|
||||
if req.CA != nil {
|
||||
err = errors.New("ca section only permitted in initca")
|
||||
return
|
||||
}
|
||||
|
||||
var key, csrPEM []byte
|
||||
g := &csr.Generator{Validator: Validator}
|
||||
csrPEM, key, err = g.ProcessRequest(&req)
|
||||
if err != nil {
|
||||
key = nil
|
||||
return
|
||||
}
|
||||
|
||||
cli.PrintCert(key, csrPEM, nil)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validator does nothing and will never return an error. It exists because creating a
|
||||
// csr.Generator requires a Validator.
|
||||
func Validator(req *csr.CertificateRequest) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'genkey'
|
||||
var Command = &cli.Command{UsageText: genkeyUsageText, Flags: genkeyFlags, Main: genkeyMain}
|
|
@ -1,31 +0,0 @@
|
|||
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/cli/info",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/info",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/api/client:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/sign: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/info: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"],
|
||||
)
|
|
@ -1,110 +0,0 @@
|
|||
// Package info implements the info command.
|
||||
package info
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/cfssl/api/client"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/cli/sign"
|
||||
"github.com/cloudflare/cfssl/errors"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/info"
|
||||
|
||||
goerr "errors"
|
||||
)
|
||||
|
||||
var infoUsageTxt = `cfssl info -- get info about a remote signer
|
||||
|
||||
Usage:
|
||||
|
||||
Get info about a remote signer:
|
||||
cfssl info -remote remote_host [-label label] [-profile profile] [-label label]
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var infoFlags = []string{"remote", "label", "profile", "config"}
|
||||
|
||||
func getInfoFromRemote(c cli.Config) (resp *info.Resp, err error) {
|
||||
req := new(info.Req)
|
||||
req.Label = c.Label
|
||||
req.Profile = c.Profile
|
||||
|
||||
cert, err := helpers.LoadClientCertificate(c.MutualTLSCertFile, c.MutualTLSKeyFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
remoteCAs, err := helpers.LoadPEMCertPool(c.TLSRemoteCAs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
serv := client.NewServerTLS(c.Remote, helpers.CreateTLSConfig(remoteCAs, cert))
|
||||
reqJSON, _ := json.Marshal(req)
|
||||
resp, err = serv.Info(reqJSON)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = helpers.ParseCertificatePEM([]byte(resp.Certificate))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getInfoFromConfig(c cli.Config) (resp *info.Resp, err error) {
|
||||
s, err := sign.SignerFromConfig(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req := new(info.Req)
|
||||
req.Label = c.Label
|
||||
req.Profile = c.Profile
|
||||
|
||||
resp, err = s.Info(*req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func infoMain(args []string, c cli.Config) (err error) {
|
||||
if len(args) > 0 {
|
||||
return goerr.New("argument is provided but not defined; please refer to the usage by flag -h.")
|
||||
}
|
||||
|
||||
var resp *info.Resp
|
||||
|
||||
if c.Remote != "" {
|
||||
resp, err = getInfoFromRemote(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else if c.CFG != nil {
|
||||
resp, err = getInfoFromConfig(c)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
return goerr.New("Either -remote or -config must be given. Refer to cfssl info -h for usage.")
|
||||
}
|
||||
|
||||
respJSON, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
return errors.NewBadRequest(err)
|
||||
}
|
||||
fmt.Print(string(respJSON))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'info'
|
||||
var Command = &cli.Command{
|
||||
UsageText: infoUsageTxt,
|
||||
Flags: infoFlags,
|
||||
Main: infoMain,
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["ocspdump.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/ocspdump",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/ocspdump",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/dbconf:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/sql:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,50 +0,0 @@
|
|||
// Package ocspdump implements the ocspdump command.
|
||||
package ocspdump
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/cfssl/certdb/dbconf"
|
||||
"github.com/cloudflare/cfssl/certdb/sql"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl ocspdump'
|
||||
var ocspdumpUsageText = `cfssl ocspdump -- generates a series of concatenated OCSP responses
|
||||
for use with ocspserve from all OCSP responses in the cert db
|
||||
|
||||
Usage of ocspdump:
|
||||
cfssl ocspdump -db-config db-config
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// Flags of 'cfssl ocspdump'
|
||||
var ocspdumpFlags = []string{"db-config"}
|
||||
|
||||
// ocspdumpMain is the main CLI of OCSP dump functionality.
|
||||
func ocspdumpMain(args []string, c cli.Config) error {
|
||||
if c.DBConfigFile == "" {
|
||||
return errors.New("need DB config file (provide with -db-config)")
|
||||
}
|
||||
|
||||
db, err := dbconf.DBFromConfig(c.DBConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbAccessor := sql.NewAccessor(db)
|
||||
records, err := dbAccessor.GetUnexpiredOCSPs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, certRecord := range records {
|
||||
fmt.Printf("%s\n", base64.StdEncoding.EncodeToString([]byte(certRecord.Body)))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'ocspdump'
|
||||
var Command = &cli.Command{UsageText: ocspdumpUsageText, Flags: ocspdumpFlags, Main: ocspdumpMain}
|
|
@ -1,31 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["ocsprefresh.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/ocsprefresh",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/ocsprefresh",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/dbconf:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/sql:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,112 +0,0 @@
|
|||
// Package ocsprefresh implements the ocsprefresh command.
|
||||
package ocsprefresh
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/certdb/dbconf"
|
||||
"github.com/cloudflare/cfssl/certdb/sql"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/ocsp"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl ocsprefresh'
|
||||
var ocsprefreshUsageText = `cfssl ocsprefresh -- refreshes the ocsp_responses table
|
||||
with new OCSP responses for all known unexpired certificates
|
||||
|
||||
Usage of ocsprefresh:
|
||||
cfssl ocsprefresh -db-config db-config -ca cert -responder cert -responder-key key [-interval 96h]
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// Flags of 'cfssl ocsprefresh'
|
||||
var ocsprefreshFlags = []string{"ca", "responder", "responder-key", "db-config", "interval"}
|
||||
|
||||
// ocsprefreshMain is the main CLI of OCSP refresh functionality.
|
||||
func ocsprefreshMain(args []string, c cli.Config) error {
|
||||
if c.DBConfigFile == "" {
|
||||
return errors.New("need DB config file (provide with -db-config)")
|
||||
}
|
||||
|
||||
if c.ResponderFile == "" {
|
||||
return errors.New("need responder certificate (provide with -responder)")
|
||||
}
|
||||
|
||||
if c.ResponderKeyFile == "" {
|
||||
return errors.New("need responder key (provide with -responder-key)")
|
||||
}
|
||||
|
||||
if c.CAFile == "" {
|
||||
return errors.New("need CA certificate (provide with -ca)")
|
||||
}
|
||||
|
||||
s, err := SignerFromConfig(c)
|
||||
if err != nil {
|
||||
log.Critical("Unable to create OCSP signer: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
db, err := dbconf.DBFromConfig(c.DBConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbAccessor := sql.NewAccessor(db)
|
||||
certs, err := dbAccessor.GetUnexpiredCertificates()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set an expiry timestamp for all certificates refreshed in this batch
|
||||
ocspExpiry := time.Now().Add(c.Interval)
|
||||
for _, certRecord := range certs {
|
||||
cert, err := helpers.ParseCertificatePEM([]byte(certRecord.PEM))
|
||||
if err != nil {
|
||||
log.Critical("Unable to parse certificate: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
req := ocsp.SignRequest{
|
||||
Certificate: cert,
|
||||
Status: certRecord.Status,
|
||||
}
|
||||
|
||||
if certRecord.Status == "revoked" {
|
||||
req.Reason = int(certRecord.Reason)
|
||||
req.RevokedAt = certRecord.RevokedAt
|
||||
}
|
||||
|
||||
resp, err := s.Sign(req)
|
||||
if err != nil {
|
||||
log.Critical("Unable to sign OCSP response: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = dbAccessor.UpsertOCSP(cert.SerialNumber.String(), hex.EncodeToString(cert.AuthorityKeyId), string(resp), ocspExpiry)
|
||||
if err != nil {
|
||||
log.Critical("Unable to save OCSP response: ", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SignerFromConfig creates a signer from a cli.Config as a helper for cli and serve
|
||||
func SignerFromConfig(c cli.Config) (ocsp.Signer, error) {
|
||||
//if this is called from serve then we need to use the specific responder key file
|
||||
//fallback to key for backwards-compatibility
|
||||
k := c.ResponderKeyFile
|
||||
if k == "" {
|
||||
k = c.KeyFile
|
||||
}
|
||||
return ocsp.NewSignerFromFile(c.CAFile, c.ResponderFile, k, time.Duration(c.Interval))
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'ocsprefresh'
|
||||
var Command = &cli.Command{UsageText: ocsprefreshUsageText, Flags: ocsprefreshFlags, Main: ocsprefreshMain}
|
|
@ -1,28 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["ocspserve.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/ocspserve",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/ocspserve",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,62 +0,0 @@
|
|||
// Package ocspserve implements the ocspserve function.
|
||||
package ocspserve
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/ocsp"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl serve'
|
||||
var ocspServerUsageText = `cfssl ocspserve -- set up an HTTP server that handles OCSP requests from either a file or directly from a database (see RFC 5019)
|
||||
|
||||
Usage of ocspserve:
|
||||
cfssl ocspserve [-address address] [-port port] [-responses file] [-db-config db-config]
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// Flags used by 'cfssl serve'
|
||||
var ocspServerFlags = []string{"address", "port", "responses", "db-config"}
|
||||
|
||||
// ocspServerMain is the command line entry point to the OCSP responder.
|
||||
// It sets up a new HTTP server that responds to OCSP requests.
|
||||
func ocspServerMain(args []string, c cli.Config) error {
|
||||
var src ocsp.Source
|
||||
// serve doesn't support arguments.
|
||||
if len(args) > 0 {
|
||||
return errors.New("argument is provided but not defined; please refer to the usage by flag -h")
|
||||
}
|
||||
|
||||
if c.Responses != "" {
|
||||
s, err := ocsp.NewSourceFromFile(c.Responses)
|
||||
if err != nil {
|
||||
return errors.New("unable to read response file")
|
||||
}
|
||||
src = s
|
||||
} else if c.DBConfigFile != "" {
|
||||
s, err := ocsp.NewSourceFromDB(c.DBConfigFile)
|
||||
if err != nil {
|
||||
return errors.New("unable to read configuration file")
|
||||
}
|
||||
src = s
|
||||
} else {
|
||||
return errors.New(
|
||||
"no response file or db-config provided, please set the one of these using either -responses or -db-config flags",
|
||||
)
|
||||
}
|
||||
|
||||
log.Info("Registering OCSP responder handler")
|
||||
http.Handle(c.Path, ocsp.NewResponder(src))
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", c.Address, c.Port)
|
||||
log.Info("Now listening on ", addr)
|
||||
return http.ListenAndServe(addr, nil)
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'ocspserve'
|
||||
var Command = &cli.Command{UsageText: ocspServerUsageText, Flags: ocspServerFlags, Main: ocspServerMain}
|
|
@ -1,29 +0,0 @@
|
|||
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/cli/ocspsign",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/ocspsign",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,93 +0,0 @@
|
|||
// Package ocspsign implements the ocspsign command.
|
||||
package ocspsign
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/ocsp"
|
||||
)
|
||||
|
||||
// Usage text of 'cfssl ocspsign'
|
||||
var ocspSignerUsageText = `cfssl ocspsign -- signs an OCSP response for a given CA, cert, and status.
|
||||
Returns a base64-encoded OCSP response.
|
||||
|
||||
Usage of ocspsign:
|
||||
cfssl ocspsign -ca cert -responder cert -responder-key key -cert cert [-status status] [-reason code] [-revoked-at YYYY-MM-DD] [-interval 96h]
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
// Flags of 'cfssl ocspsign'
|
||||
var ocspSignerFlags = []string{"ca", "responder", "responder-key", "reason", "status", "revoked-at", "interval"}
|
||||
|
||||
// ocspSignerMain is the main CLI of OCSP signer functionality.
|
||||
func ocspSignerMain(args []string, c cli.Config) (err error) {
|
||||
// Read the cert to be revoked from file
|
||||
certBytes, err := ioutil.ReadFile(c.CertFile)
|
||||
if err != nil {
|
||||
log.Critical("Unable to read certificate: ", err)
|
||||
return
|
||||
}
|
||||
cert, err := helpers.ParseCertificatePEM(certBytes)
|
||||
if err != nil {
|
||||
log.Critical("Unable to parse certificate: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
req := ocsp.SignRequest{
|
||||
Certificate: cert,
|
||||
Status: c.Status,
|
||||
}
|
||||
|
||||
if c.Status == "revoked" {
|
||||
var reasonCode int
|
||||
reasonCode, err = ocsp.ReasonStringToCode(c.Reason)
|
||||
if err != nil {
|
||||
log.Critical("Invalid reason code: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
req.Reason = reasonCode
|
||||
req.RevokedAt = time.Now()
|
||||
if c.RevokedAt != "now" {
|
||||
req.RevokedAt, err = time.Parse("2006-01-02", c.RevokedAt)
|
||||
if err != nil {
|
||||
log.Critical("Malformed revocation time: ", c.RevokedAt)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s, err := SignerFromConfig(c)
|
||||
if err != nil {
|
||||
log.Critical("Unable to create OCSP signer: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := s.Sign(req)
|
||||
if err != nil {
|
||||
log.Critical("Unable to sign OCSP response: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
cli.PrintOCSPResponse(resp)
|
||||
return
|
||||
}
|
||||
|
||||
// SignerFromConfig creates a signer from a cli.Config as a helper for cli and serve
|
||||
func SignerFromConfig(c cli.Config) (ocsp.Signer, error) {
|
||||
//if this is called from serve then we need to use the specific responder key file
|
||||
//fallback to key for backwards-compatibility
|
||||
k := c.ResponderKeyFile
|
||||
if k == "" {
|
||||
k = c.KeyFile
|
||||
}
|
||||
return ocsp.NewSignerFromFile(c.CAFile, c.ResponderFile, k, time.Duration(c.Interval))
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'ocspsign'
|
||||
var Command = &cli.Command{UsageText: ocspSignerUsageText, Flags: ocspSignerFlags, Main: ocspSignerMain}
|
|
@ -1,27 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"defaults.go",
|
||||
"printdefault.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/printdefault",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/printdefault",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,49 +0,0 @@
|
|||
package printdefaults
|
||||
|
||||
var defaults = map[string]string{
|
||||
"config": `{
|
||||
"signing": {
|
||||
"default": {
|
||||
"expiry": "168h"
|
||||
},
|
||||
"profiles": {
|
||||
"www": {
|
||||
"expiry": "8760h",
|
||||
"usages": [
|
||||
"signing",
|
||||
"key encipherment",
|
||||
"server auth"
|
||||
]
|
||||
},
|
||||
"client": {
|
||||
"expiry": "8760h",
|
||||
"usages": [
|
||||
"signing",
|
||||
"key encipherment",
|
||||
"client auth"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
"csr": `{
|
||||
"CN": "example.net",
|
||||
"hosts": [
|
||||
"example.net",
|
||||
"www.example.net"
|
||||
],
|
||||
"key": {
|
||||
"algo": "ecdsa",
|
||||
"size": 256
|
||||
},
|
||||
"names": [
|
||||
{
|
||||
"C": "US",
|
||||
"ST": "CA",
|
||||
"L": "San Francisco"
|
||||
}
|
||||
]
|
||||
}
|
||||
`,
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package printdefaults
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
)
|
||||
|
||||
var printDefaultsUsage = `cfssl print-defaults -- print default configurations that can be used as a template
|
||||
|
||||
Usage of print-defaults:
|
||||
cfssl print-defaults TYPE
|
||||
|
||||
If "list" is used as the TYPE, the list of supported types will be printed.
|
||||
`
|
||||
|
||||
func printAvailable() {
|
||||
fmt.Println("Default configurations are available for:")
|
||||
for name := range defaults {
|
||||
fmt.Println("\t" + name)
|
||||
}
|
||||
}
|
||||
|
||||
func printDefaults(args []string, c cli.Config) (err error) {
|
||||
arg, _, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if arg == "list" {
|
||||
printAvailable()
|
||||
} else {
|
||||
if config, ok := defaults[arg]; !ok {
|
||||
printAvailable()
|
||||
} else {
|
||||
fmt.Println(config)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'print-defaults'
|
||||
var Command = &cli.Command{
|
||||
UsageText: printDefaultsUsage,
|
||||
Flags: []string{},
|
||||
Main: printDefaults,
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
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/cli/revoke",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/revoke",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/dbconf:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/certdb/sql:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,66 +0,0 @@
|
|||
// Package revoke implements the revoke command.
|
||||
package revoke
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/cloudflare/cfssl/certdb/dbconf"
|
||||
"github.com/cloudflare/cfssl/certdb/sql"
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/ocsp"
|
||||
)
|
||||
|
||||
var revokeUsageTxt = `cfssl revoke -- revoke a certificate in the certificate store
|
||||
|
||||
Usage:
|
||||
|
||||
Revoke a certificate:
|
||||
cfssl revoke -db-config config_file -serial serial -aki authority_key_id [-reason reason]
|
||||
|
||||
Reason can be an integer code or a string in ReasonFlags in RFC 5280
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var revokeFlags = []string{"serial", "reason"}
|
||||
|
||||
func revokeMain(args []string, c cli.Config) error {
|
||||
if len(args) > 0 {
|
||||
return errors.New("argument is provided but not defined; please refer to the usage by flag -h")
|
||||
}
|
||||
|
||||
if len(c.Serial) == 0 {
|
||||
return errors.New("serial number is required but not provided")
|
||||
}
|
||||
|
||||
if len(c.AKI) == 0 {
|
||||
return errors.New("authority key id is required but not provided")
|
||||
}
|
||||
|
||||
if c.DBConfigFile == "" {
|
||||
return errors.New("need DB config file (provide with -db-config)")
|
||||
}
|
||||
|
||||
db, err := dbconf.DBFromConfig(c.DBConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbAccessor := sql.NewAccessor(db)
|
||||
|
||||
reasonCode, err := ocsp.ReasonStringToCode(c.Reason)
|
||||
if err != nil {
|
||||
log.Error("Invalid reason code: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return dbAccessor.RevokeCertificate(c.Serial, c.AKI, reasonCode)
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'revoke'
|
||||
var Command = &cli.Command{
|
||||
UsageText: revokeUsageTxt,
|
||||
Flags: revokeFlags,
|
||||
Main: revokeMain,
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
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/cli/scan",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/scan",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli: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"],
|
||||
)
|
|
@ -1,123 +0,0 @@
|
|||
package scan
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
"github.com/cloudflare/cfssl/scan"
|
||||
)
|
||||
|
||||
var scanUsageText = `cfssl scan -- scan a host for issues
|
||||
Usage of scan:
|
||||
cfssl scan [-family regexp] [-scanner regexp] [-timeout duration] [-ip IPAddr] [-num-workers num] [-max-hosts num] [-csv hosts.csv] HOST+
|
||||
cfssl scan -list
|
||||
|
||||
Arguments:
|
||||
HOST: Host(s) to scan (including port)
|
||||
Flags:
|
||||
`
|
||||
var scanFlags = []string{"list", "family", "scanner", "timeout", "ip", "ca-bundle", "num-workers", "csv", "max-hosts"}
|
||||
|
||||
func printJSON(v interface{}) {
|
||||
b, err := json.MarshalIndent(v, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Printf("%s\n\n", b)
|
||||
}
|
||||
|
||||
type context struct {
|
||||
sync.WaitGroup
|
||||
c cli.Config
|
||||
hosts chan string
|
||||
}
|
||||
|
||||
func newContext(c cli.Config, numWorkers int) *context {
|
||||
ctx := &context{
|
||||
c: c,
|
||||
hosts: make(chan string, numWorkers),
|
||||
}
|
||||
ctx.Add(numWorkers)
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
go ctx.runWorker()
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (ctx *context) runWorker() {
|
||||
for host := range ctx.hosts {
|
||||
fmt.Printf("Scanning %s...\n", host)
|
||||
results, err := scan.Default.RunScans(host, ctx.c.IP, ctx.c.Family, ctx.c.Scanner, ctx.c.Timeout)
|
||||
fmt.Printf("=== %s ===\n", host)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
printJSON(results)
|
||||
}
|
||||
}
|
||||
ctx.Done()
|
||||
}
|
||||
|
||||
func parseCSV(hosts []string, csvFile string, maxHosts int) ([]string, error) {
|
||||
f, err := os.Open(csvFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r := csv.NewReader(f)
|
||||
for err == nil && len(hosts) < maxHosts {
|
||||
var record []string
|
||||
record, err = r.Read()
|
||||
hosts = append(hosts, record[len(record)-1])
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return hosts, err
|
||||
}
|
||||
|
||||
func scanMain(args []string, c cli.Config) (err error) {
|
||||
if c.List {
|
||||
printJSON(scan.Default)
|
||||
} else {
|
||||
if err = scan.LoadRootCAs(c.CABundleFile); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) >= c.MaxHosts {
|
||||
log.Warningf("Only scanning max-hosts=%d out of %d args given", c.MaxHosts, len(args))
|
||||
args = args[:c.MaxHosts]
|
||||
} else if c.CSVFile != "" {
|
||||
args, err = parseCSV(args, c.CSVFile, c.MaxHosts)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx := newContext(c, c.NumWorkers)
|
||||
// Execute for each HOST argument given
|
||||
for len(args) > 0 {
|
||||
var host string
|
||||
host, args, err = cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.hosts <- host
|
||||
}
|
||||
close(ctx.hosts)
|
||||
ctx.Wait()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'scan'
|
||||
var Command = &cli.Command{UsageText: scanUsageText, Flags: scanFlags, Main: scanMain}
|
|
@ -1,31 +0,0 @@
|
|||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["selfsign.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/github.com/cloudflare/cfssl/cli/selfsign",
|
||||
importpath = "github.com/cloudflare/cfssl/cli/selfsign",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//vendor/github.com/cloudflare/cfssl/cli:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/cli/genkey: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/helpers:go_default_library",
|
||||
"//vendor/github.com/cloudflare/cfssl/selfsign: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"],
|
||||
)
|
|
@ -1,120 +0,0 @@
|
|||
// Package selfsign implements the selfsign command.
|
||||
package selfsign
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/cloudflare/cfssl/cli"
|
||||
"github.com/cloudflare/cfssl/cli/genkey"
|
||||
"github.com/cloudflare/cfssl/config"
|
||||
"github.com/cloudflare/cfssl/csr"
|
||||
"github.com/cloudflare/cfssl/helpers"
|
||||
"github.com/cloudflare/cfssl/selfsign"
|
||||
)
|
||||
|
||||
var selfSignUsageText = `cfssl selfsign -- generate a new self-signed key and signed certificate
|
||||
|
||||
Usage of gencert:
|
||||
cfssl selfsign HOSTNAME CSRJSON
|
||||
|
||||
WARNING: this should ONLY be used for testing. This should never be
|
||||
used in production.
|
||||
|
||||
WARNING: self-signed certificates are insecure; they do not provide
|
||||
the authentication required for secure systems. Use these at your own
|
||||
risk.
|
||||
|
||||
Arguments:
|
||||
HOSTNAME: Hostname for the cert
|
||||
CSRJSON: JSON file containing the request, use '-' for reading JSON from stdin
|
||||
|
||||
Flags:
|
||||
`
|
||||
|
||||
var selfSignFlags = []string{"config"}
|
||||
|
||||
func selfSignMain(args []string, c cli.Config) (err error) {
|
||||
if c.Hostname == "" && !c.IsCA {
|
||||
c.Hostname, args, err = cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
csrFile, args, err := cli.PopFirstArgument(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
return errors.New("too many arguments are provided, please check with usage")
|
||||
}
|
||||
|
||||
csrFileBytes, err := cli.ReadStdin(csrFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var req = csr.New()
|
||||
err = json.Unmarshal(csrFileBytes, req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var key, csrPEM []byte
|
||||
g := &csr.Generator{Validator: genkey.Validator}
|
||||
csrPEM, key, err = g.ProcessRequest(req)
|
||||
if err != nil {
|
||||
key = nil
|
||||
return
|
||||
}
|
||||
|
||||
priv, err := helpers.ParsePrivateKeyPEM(key)
|
||||
if err != nil {
|
||||
key = nil
|
||||
return
|
||||
}
|
||||
|
||||
var profile *config.SigningProfile
|
||||
|
||||
// If there is a config, use its signing policy. Otherwise, leave policy == nil
|
||||
// and NewSigner will use DefaultConfig().
|
||||
if c.CFG != nil {
|
||||
if c.Profile != "" && c.CFG.Signing.Profiles != nil {
|
||||
profile = c.CFG.Signing.Profiles[c.Profile]
|
||||
}
|
||||
}
|
||||
|
||||
if profile == nil {
|
||||
profile = config.DefaultConfig()
|
||||
profile.Expiry = 2190 * time.Hour
|
||||
}
|
||||
|
||||
cert, err := selfsign.Sign(priv, csrPEM, profile)
|
||||
if err != nil {
|
||||
key = nil
|
||||
priv = nil
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, `*** WARNING ***
|
||||
|
||||
Self-signed certificates are dangerous. Use this self-signed
|
||||
certificate at your own risk.
|
||||
|
||||
It is strongly recommended that these certificates NOT be used
|
||||
in production.
|
||||
|
||||
*** WARNING ***
|
||||
|
||||
`)
|
||||
cli.PrintCert(key, csrPEM, cert)
|
||||
return
|
||||
}
|
||||
|
||||
// Command assembles the definition of Command 'selfsign'
|
||||
var Command = &cli.Command{UsageText: selfSignUsageText, Flags: selfSignFlags, Main: selfSignMain}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue