| #!/usr/bin/env python |
| |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| import argparse |
| import base64 |
| import os |
| import subprocess |
| import sys |
| import tempfile |
| |
| # we to import Request and urlopen differently for python 2 and 3 |
| try: |
| from urllib.request import Request |
| from urllib.request import urlopen |
| except ImportError: |
| from urllib2 import Request |
| from urllib2 import urlopen |
| |
| DESCRIPTION = """Encrypt a secret for Zuul. |
| |
| This program fetches a project-specific public key from a Zuul server and |
| uses that to encrypt a secret. The only pre-requisite is an installed |
| OpenSSL binary. |
| """ |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description=DESCRIPTION) |
| parser.add_argument('url', |
| help="The base URL of the zuul server and tenant. " |
| "E.g., https://zuul.example.com/tenant-name") |
| # TODO(jeblair,mordred): When projects have canonical names, use that here. |
| # TODO(jeblair): Throw a fit if SSL is not used. |
| parser.add_argument('source', |
| help="The Zuul source of the project.") |
| parser.add_argument('project', |
| help="The name of the project.") |
| parser.add_argument('--infile', |
| default=None, |
| help="A filename whose contents will be encrypted. " |
| "If not supplied, the value will be read from " |
| "standard input.") |
| parser.add_argument('--outfile', |
| default=None, |
| help="A filename to which the encrypted value will be " |
| "written. If not supplied, the value will be written " |
| "to standard output.") |
| args = parser.parse_args() |
| |
| req = Request("%s/keys/%s/%s.pub" % ( |
| args.url, args.source, args.project)) |
| pubkey = urlopen(req) |
| |
| if args.infile: |
| with open(args.infile) as f: |
| plaintext = f.read() |
| else: |
| plaintext = sys.stdin.read() |
| |
| pubkey_file = tempfile.NamedTemporaryFile(delete=False) |
| try: |
| pubkey_file.write(pubkey.read()) |
| pubkey_file.close() |
| |
| p = subprocess.Popen(['openssl', 'rsautl', '-encrypt', |
| '-oaep', '-pubin', '-inkey', |
| pubkey_file.name], |
| stdin=subprocess.PIPE, |
| stdout=subprocess.PIPE) |
| (stdout, stderr) = p.communicate(plaintext.encode("utf-8")) |
| if p.returncode != 0: |
| raise Exception("Return code %s from openssl" % p.returncode) |
| ciphertext = base64.b64encode(stdout) |
| finally: |
| os.unlink(pubkey_file.name) |
| |
| if args.outfile: |
| with open(args.outfile, "wb") as f: |
| f.write(ciphertext) |
| else: |
| print(ciphertext.decode("utf-8")) |
| |
| |
| if __name__ == '__main__': |
| main() |