Skip to content

Add --askpin option #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Add --askpin option #130

wants to merge 1 commit into from

Conversation

miska
Copy link

@miska miska commented Sep 18, 2019

Adding --askpin option modeled after --askpass, letting people enter pin early
int he startup or more importantly allow them to keep a password in separate
file to simplify unattended setup.

Signed-off-by: Michal Hrusecky Michal@Hrusecky.net

Thank you for your contribution

You are welcome to open PR, but they are used for discussion only. All
patches must eventually go to the openvpn-devel mailing list for review:

Please send your patch using git-send-email. For example to send your latest commit to the list:

$ git send-email --to=openvpn-devel@lists.sourceforge.net HEAD~1

For details, see these Wiki articles:

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
Adding --askpin option modeled after --askpass, letting people enter pin early
int he startup or more importantly allow them to keep a password in separate
file to simplify unattended setup.

Signed-off-by: Michal Hrusecky <Michal@Hrusecky.net>
token_pass.nocache = true;

if (!strlen(token_pass.password))
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass

@ordex
Copy link
Member

ordex commented Sep 17, 2022

It wasn't clear that this was about PKCS#11. How about renaming the option to --pkcs11-askpin ? I think it'll be much more self explanatory. At the same time I'd extend the doc a little bit to mention what the pin is for. May be obvious for you or me looking at the code, but not for the casual reader.

@miska if still interested, how about addressing these changes and sending the patch to the mailing list, please?

@ordex
Copy link
Member

ordex commented Oct 4, 2022

@miska @finda841 any motivation in bringing this forward? Thanks!

@becm
Copy link
Contributor

becm commented Dec 12, 2022

Since it's a violation of security (to a varying degree, the file/config may be on an encrypted file system), it may be nice to go the full way of

pkcs11-pin mypin.txt

and

<pkcs11-pin>
123456
</pkcs11-pin>

A process who can read the PIN file must have access to the config (and vice versa) anyway.

And it's still (kind of) more secure than askpass. ☺

@R0Wi
Copy link

R0Wi commented Jul 31, 2023

Would really love to see this feature and I would volunteer to bring the development forward. @ordex @becm could you please summarize your preferred way of implementing this? For me a new option --pkcs11-askpin would totally make sense, since there is already a --askpass option which does more or less the same to handle certificate passwords (instead of PKCS11 pins). Looking forward to your feedback!

@ordex
Copy link
Member

ordex commented Jul 31, 2023

Maybe @dsommers could be more helpful here

@becm
Copy link
Contributor

becm commented Aug 6, 2023

For me, it just felt like this should behave more like a config option that supports inline PIN data.
The triplet (pkcs11-provider, pkcs11-id, pkcs11-pin) is then contained without external dependencies.

Required presence of the token should still make this more secure than traditional askpass use.

The behavioral difference would also enhance the use of a new option (pkcs11-pin) instead of just re-purposing the askpass content as PIN in PKCS11 mode.

I'm not in any position to advise on or green-light implementation though. 😉

@luqtd
Copy link

luqtd commented May 5, 2025

I would also like to bring this development forward. Is it possible? @dsommers @ordex @becm

Would it be possible to also accommodate @R0Wi’s request? I can volunteer to make the changes myself, as long as someone can explain the correct way to do it.

Would really love to see this feature and I would volunteer to bring the development forward. @ordex @becm could you please summarize your preferred way of implementing this? For me a new option --pkcs11-askpin would totally make sense, since there is already a --askpass option which does more or less the same to handle certificate passwords (instead of PKCS11 pins). Looking forward to your feedback!

@luqtd luqtd mentioned this pull request May 7, 2025
@R0Wi
Copy link

R0Wi commented May 7, 2025

If anyone is interested in a workaround: In the meantime I solved this by using the OpenVPN Management Interface to provide the PKCS11 pin at runtime.

Step 1: Add this to your openvpn.conf

management 127.0.0.1 7505
management-query-passwords

Step 2: Create a tcp client which connects to the OpenVPN Management Interface on 127.0.0.1:7505. I decided to go with a little python script provide-pin.py:

import socket
import time
import argparse
import logging
import sys
import signal
from logging.handlers import RotatingFileHandler

EXPECTED_MESSAGE = ">PASSWORD:Need 'OpenPGP card (User PIN) token' password"
running = True

def read_pin_from_file(file):
    with open(file) as pin_file_res:
        return pin_file_res.read().strip()
    
def read_ovpn_management_settings(file):
    with open(file) as ovpn_file_res:
        content_lines = ovpn_file_res.read().split("\n")
        for line in content_lines:
            if line.startswith("management "):
                parts = line.split()
                # management <listen_addr> <port>
                return parts[1], int(parts[2])

def handle_sigterm(signum, frame):
    global running
    logging.info("Handling SIGTERM")
    running = False
    raise KeyboardInterrupt("SIGTERM received, shutting down...")

def main(logfile, pinfile, ovpnconfig):
    global running

    if logfile:
        handler = RotatingFileHandler(logfile, maxBytes=5*1024*1024, backupCount=3)
        logging.basicConfig(handlers=[handler], level=logging.INFO, format='%(asctime)s %(message)s')
    else:
        logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(asctime)s %(message)s')

    pin = read_pin_from_file(pinfile)
    management_addr, management_port = read_ovpn_management_settings(ovpnconfig)
    
    signal.signal(signal.SIGTERM, handle_sigterm)

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        while running:
            try:
                s.connect((management_addr, management_port))
                break
            except ConnectionRefusedError:
                logging.info(f"Connection refused to {management_addr}:{management_port}, retrying in 2 seconds...")
                time.sleep(2)
                
        logging.info("Connected to OVPN management interface, start listening ...")
        
        while running:
            try:
                data = s.recv(1024)
            except KeyboardInterrupt:
                logging.info("KeyboardInterrupt received, exiting...")
                break
            if not data:
                logging.info("Connection closed, exiting...")
                break
            messages = filter(lambda msg: msg != '', [message.strip() for message in data.decode('utf-8').split("\n")])
            for message in messages:
                logging.info(f"Received: {message}")
                if message == EXPECTED_MESSAGE:
                    logging.info("Providing password...")
                    answer = "password 'OpenPGP card (User PIN) token' '" + pin + "'\n"
                    s.sendall(answer.encode('utf-8'))
                    
    logging.info("Program ended properly")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Provide PCKS11 PIN for OpenVPN')
    parser.add_argument('--logfile', type=str, help='Path to the log file. If not set, messages will be printed to stdout')
    parser.add_argument('--pinfile', type=str, help='Path to the pin file where the pin to be provided is stored (default: /etc/openvpn/pin)', default='/etc/openvpn/pin')
    parser.add_argument('--ovpnconfig', type=str, help='Path to the OpenVPN config file (default: /etc/openvpn/client.conf)', default='/etc/openvpn/client.conf')
    args = parser.parse_args()
    main(args.logfile, args.pinfile, args.ovpnconfig)

Adjust the EXPECTED_MESSAGE variable to your needs.

Step 3: Ensure that the client is started together with your openvpn instance. If you're using systemd, use a service file like this

[Unit]
Description=Provide PKCS11 PIN for OpenVPN
After=network.target openvpn@client.service
Requires=openvpn@client.service

[Service]
ExecStart=/usr/bin/python3 /etc/openvpn/provide-pin.py --logfile /var/log/provide-pin.log --pinfile /etc/openvpn/pin --ovpnconfig /etc/openvpn/client.conf
Restart=always
User=root
Group=root

[Install]
WantedBy=multi-user.target

Again, adjust the parameters to your needs. You might also want to add a dependency to the openvpn service itself by creating /etc/systemd/system/openvpn@client.service.d/override.conf with the following content

[Unit]
Requires=provide-pin.service

Don't forget to do a systemctl daemon-reload.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants