Add easywks import CLI command
This commit is contained in:
parent
0a337f90ee
commit
492b823215
6 changed files with 51 additions and 6 deletions
13
README.md
13
README.md
|
@ -303,6 +303,19 @@ Encrypt live everybody is.
|
|||
|
||||
```
|
||||
|
||||
## Manual Key Import
|
||||
|
||||
In addition to WKS, EasyWKS also provides a command line interface for
|
||||
importing keys from standard input. This feature is mainly intended
|
||||
to be used for technical email accounts where using WKS might prove to
|
||||
be difficult:
|
||||
|
||||
```console?prompt=$,
|
||||
$ cat pubkey.asc | easywks import
|
||||
Skipping foreign email john.doe@notmydepartment.org
|
||||
Imported key A58D3221F8079F35FF084890505A563492A56583 for email john.doe@example.org
|
||||
```
|
||||
|
||||
[wkd]: https://wiki.gnupg.org/WKD
|
||||
[wks]: https://wiki.gnupg.org/WKS
|
||||
[ietf]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-12
|
||||
|
|
|
@ -96,7 +96,7 @@ def remove_pending_key(domain, nonce):
|
|||
os.unlink(keyfile)
|
||||
|
||||
|
||||
def clean_stale_requests():
|
||||
def clean_stale_requests(args):
|
||||
stale = (datetime.utcnow() - timedelta(seconds=Config.pending_lifetime)).timestamp()
|
||||
for domain in Config.domains:
|
||||
pending_dir = os.path.join(Config.working_directory, domain, 'pending')
|
||||
|
|
|
@ -38,7 +38,7 @@ def hu(domain: str, userhash: str):
|
|||
abort(404, 'Not Found')
|
||||
|
||||
|
||||
def run_server():
|
||||
def run_server(args):
|
||||
run(host=Config.httpd['host'], port=Config.httpd['port'])
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class LmtpdController(Controller):
|
|||
return LMTP(handler=self.handler, ident=f'EasyWKS {version}', loop=self.loop)
|
||||
|
||||
|
||||
def run_lmtpd():
|
||||
def run_lmtpd(args):
|
||||
controller = LmtpdController(handler=LmtpMailServer(), hostname=Config.lmtpd['host'], port=Config.lmtpd['port'])
|
||||
controller.start()
|
||||
asyncio.get_event_loop().run_forever()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
from .config import Config
|
||||
from .files import init_working_directory, clean_stale_requests
|
||||
from .process import process_mail_from_stdin
|
||||
from .process import process_mail_from_stdin, process_key_from_stdin
|
||||
from .httpd import run_server
|
||||
from .lmtpd import run_lmtpd
|
||||
|
||||
|
@ -32,6 +32,11 @@ def parse_arguments():
|
|||
server = sp.add_parser('lmtpd', help='Run a LMTP server to receive mails from your MTA. Also see process.')
|
||||
server.set_defaults(fn=run_lmtpd)
|
||||
|
||||
imp = sp.add_parser('import', help='Import a public key from stdin directly into the WKD without WKS verification.')
|
||||
imp.add_argument('--uid', '-u', type=str, action='append',
|
||||
help='Limit import to a subset of the key\'s UIDs. Can be provided multiple times.')
|
||||
imp.set_defaults(fn=process_key_from_stdin)
|
||||
|
||||
return ap.parse_args(sys.argv[1:])
|
||||
|
||||
|
||||
|
@ -44,7 +49,7 @@ def main():
|
|||
Config.load_config(conf)
|
||||
init_working_directory()
|
||||
if args.fn:
|
||||
args.fn()
|
||||
args.fn(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -7,6 +7,7 @@ from .config import Config
|
|||
from .files import read_pending_key, write_public_key, remove_pending_key, write_pending_key
|
||||
from .types import SubmissionRequest, ConfirmationResponse, PublishResponse, ConfirmationRequest, EasyWksError,\
|
||||
XLOOP_HEADER
|
||||
from .types import fingerprint
|
||||
|
||||
from email.message import MIMEPart, Message
|
||||
from email.parser import BytesParser
|
||||
|
@ -172,6 +173,32 @@ def process_mail(mail: bytes):
|
|||
method(rmsg)
|
||||
|
||||
|
||||
def process_mail_from_stdin():
|
||||
def process_mail_from_stdin(args):
|
||||
mail = sys.stdin.read().encode()
|
||||
process_mail(mail)
|
||||
|
||||
|
||||
def process_key_from_stdin(args):
|
||||
try:
|
||||
pubkey, _ = PGPKey.from_blob(sys.stdin.read())
|
||||
except PGPError:
|
||||
raise EasyWksError('Input is not a valid public key.')
|
||||
if not pubkey.is_public:
|
||||
raise EasyWksError('Input is not a valid public key.')
|
||||
|
||||
for uid in pubkey.userids:
|
||||
# Skip user attributes (e.g. photo ids)
|
||||
if not uid.is_uid or len(uid.email) == 0:
|
||||
continue
|
||||
# If a UID filter was provided on the command line, apply it
|
||||
if args.uid is not None and len(args.uid) > 0 and uid.email not in args.uid:
|
||||
print(f'Skipping ignored email {uid.email}')
|
||||
continue
|
||||
local, domain = uid.email.split('@', 1)
|
||||
# Skip keys we're not responsible for
|
||||
if domain not in Config.domains:
|
||||
print(f'Skipping foreign email {uid.email}')
|
||||
continue
|
||||
# All checks passed, importing key
|
||||
write_public_key(domain, uid.email, pubkey)
|
||||
print(f'Imported key {fingerprint(pubkey)} for email {uid.email}')
|
||||
|
|
Loading…
Reference in a new issue