Skip to content
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

OpenVPN Client Export #368

Open
VirtualSamuraii opened this issue Jul 2, 2023 · 3 comments
Open

OpenVPN Client Export #368

VirtualSamuraii opened this issue Jul 2, 2023 · 3 comments
Labels
backlog Issues backlogged for inclusion in future releases feature request New feature or request

Comments

@VirtualSamuraii
Copy link

Hi there, thank you for your amazing work with this API.

As i'm trying to automate the process of creating individual access to a VPN server, I have developed a PHP script that creates a user certificate using the API endpoints System > Certificates.

Is there any available endpoint allowing me to export the vpn client config using this certificate ?

@VirtualSamuraii VirtualSamuraii added backlog Issues backlogged for inclusion in future releases feature request New feature or request labels Jul 2, 2023
@mrizkihidayat66
Copy link

mrizkihidayat66 commented Dec 18, 2024

usr\local\pkg\RESTAPI\Endpoints\VPNOpenVPNExportsEndpoint.inc

<?php

namespace RESTAPI\Endpoints;

require_once 'RESTAPI/autoloader.inc';

use RESTAPI\Core\Endpoint;

/**
 * Defines an Endpoint for interacting with a single OpenVPNExport Model object at
 * /api/v2/vpn/openvpn/exports.
 */
class VPNOpenVPNExportsEndpoint extends Endpoint {
    public function __construct() {
        # Set Endpoint attributes
        $this->url = '/api/v2/vpn/openvpn/exports';
        $this->model_name = 'OpenVPNExport';
        $this->request_method_options = ['GET'];
        $this->many = true;

        # Construct the parent Endpoint object
        parent::__construct();
    }
}

usr\local\pkg\RESTAPI\Endpoints\VPNOpenVPNExportEndpoint.inc

<?php

namespace RESTAPI\Endpoints;

require_once 'RESTAPI/autoloader.inc';

use RESTAPI\Core\Endpoint;

/**
 * Defines an Endpoint for interacting with a single OpenVPNExport Model object at
 * /api/v2/vpn/openvpn/export.
 */
class VPNOpenVPNExportEndpoint extends Endpoint {
    public function __construct() {
        # Set Endpoint attributes
        $this->url = '/api/v2/vpn/openvpn/export';
        $this->model_name = 'OpenVPNExport';
        $this->request_method_options = ['GET', 'PATCH'];

        # Construct the parent Endpoint object
        parent::__construct();
    }
}

usr\local\pkg\RESTAPI\Models\OpenVPNExport.inc

<?php

namespace RESTAPI\Models;

use RESTAPI\Core\Model;
use RESTAPI\Fields\BooleanField;
use RESTAPI\Fields\ForeignModelField;
use RESTAPI\Fields\IntegerField;
use RESTAPI\Fields\PortField;
use RESTAPI\Fields\ObjectField;
use RESTAPI\Fields\StringField;
use RESTAPI\Responses\ConflictError;
use RESTAPI\Responses\ValidationError;
use RESTAPI\Validators\HostnameValidator;
use RESTAPI\Validators\IPAddressValidator;

/**
 * Defines a Model that represents OpenVPN Export.
 */
class OpenVPNExport extends Model {
    const DMYPWD = "********";

    //public ForeignModelField $server;
    public IntegerField $server;
    public StringField $useaddr;
    public StringField $useaddr_hostname;
    public StringField $verifyservercn;
    public BooleanField $blockoutsidedns;
    public BooleanField $legacy;
    public BooleanField $silent;
    public StringField $bindmode;
    # SKIP: public BooleanField $usepkcs11;
    public BooleanField $usetoken;
    public BooleanField $usepass;
    public StringField $pass;
    public StringField $p12encryption;
    public BooleanField $useproxy;
    public StringField $useproxytype;
    public StringField $proxyaddr;
    public PortField $proxyport;
    public StringField $useproxypass;
    public StringField $proxyuser;
    public StringField $proxypass;
    public StringField $advancedoptions;
    public ObjectField $users;

    public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], mixed ...$options) {
        # Set model attributes
        $this->config_path = 'installedpackages/vpn_openvpn_export/serverconfig/item';
        $this->packages = ['pfSense-pkg-openvpn-client-export'];
        $this->package_includes = ['openvpn-client-export.inc'];
        $this->always_apply = true;
        $this->many = true;

        # Set model fields
        //$this->server = new ForeignModelField(
        //    model_name: 'OpenVPNServer',
        //    model_field: 'vpnid',
        //    default: null,
        //    allow_empty: true,
        //    allow_null: true,
        //    many: true,
        //    help_text: 'The VPN ID of the OpenVPN server this client export corresponds to.',
        //);
        $this->server = new IntegerField(
            unique: true,
            default_callable: 'get_next_vpn_id',
            editable: true,
            read_only: false,
            help_text: 'The unique ID for this OpenVPN server. This value is assigned by the ' .
                'system and cannot be changed.',
        );

        # Client Connection Behavior
        $this->useaddr = new StringField(
            required: true,
            choices: ['serveraddr', 'servermagic', 'servermagichost', 'serverhostname', 'other'],
            help_text: 'Host Name Resolution'
        );
        $this->useaddr_hostname = new StringField(
            required: true,
            allow_empty: false,
            conditions: ['useaddr' => 'other'],
            validators: [new HostnameValidator(allow_hostname: true, allow_domain: true, allow_fqdn: true)],
            help_text: 'Host Name'
        );
        $this->verifyservercn = new StringField(
            default: 'auto',
            choices: ['auto', 'none'],
            help_text: 'Verify Server CN'
        );
        $this->blockoutsidedns = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Block Outside DNS'
        );
        $this->legacy = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Legacy Client'
        );
        $this->silent = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Silent Installer'
        );
        $this->bindmode = new StringField(
            default: 'nobind',
            choices: ['nobind', 'lport0', 'bind'],
            help_text: 'Bind Mode'
        );

        # Certificate Export Options
        $this->usetoken = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Microsoft Certificate Storage'
        );
        $this->usepass = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Password Protect Certificate'
        );
        $this->pass = new StringField(
            required: true,
            default: null,
            conditions: ['usepass' => true],
            help_text: 'Certificate Password'
        );
        $this->p12encryption = new StringField(
            default: 'high',
            choices: ['high', 'low', 'legacy'],
            help_text: 'PKCS#12 Encryption'
        );

        # Proxy Options
        $this->useproxy = new BooleanField(
            default: false,
            indicates_true: 'yes',
            indicates_false: '',
            help_text: 'Use A Proxy'
        );
        $this->useproxytype = new StringField(
            default: 'http',
            choices: ['http', 'socks'],
            conditions: ['useproxy' => true],
            help_text: 'Proxy Type'
        );
        $this->proxyaddr = new StringField(
            required: true,
            allow_empty: false,
            default: null,
            validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: false)],
            conditions: ['useproxy' => true],
            help_text: 'Proxy IP Address',
        );
        $this->proxyport = new PortField(
            required: true,
            unique: true,
            default: null,
            allow_alias: false,
            allow_range: false,
            conditions: ['useproxy' => true],
            help_text: 'Proxy Port',
        );
        $this->useproxypass = new StringField(
            required: true,
            default: null,
            choices: ['none', 'basic', 'ntlm'],
            conditions: ['useproxy' => true],
            help_text: 'Proxy Authentication'
        );
        $this->proxyuser = new StringField(
            required: true,
            default: null,
            conditions: ['useproxy' => true, 'useproxypass' => ['basic', 'ntlm']],
            help_text: 'Proxy Username'
        );
        $this->proxypass = new StringField(
            required: true,
            conditions: ['useproxy' => true, 'useproxypass' => ['basic', 'ntlm']],
            help_text: 'Proxy Password'
        );

        # Advanced
        $this->advancedoptions = new StringField(
            default: [],
            allow_empty: true,
            many: true,
            delimiter: ';',
            help_text: 'Additional options to add to the OpenVPN client export configuration.',
        );

        # OpenVPN Clients
        $this->users = new ObjectField(
            default_callable: 'get_user_certificate',
            allow_empty: true,
            many: true,
            many_minimum: 0,
            help_text: '',
        );

        parent::__construct($id, $parent_id, $data, ...$options);
    }

    /**
     * Obtains the next available VPN ID.
     * @return integer The next available ID number.
     */
    public function get_next_vpn_id(): int {
        return openvpn_vpnid_next();
    }

    /**
     * 
     */
    public function get_user_certificate(): array {
        $users = [];
        foreach (User::read_all()->model_objects as $user) {
            $users[] = $user;
        }
        return $users;
    }

    /**
     * Applies changes to this OpenVPN Server.
     */
    public function apply(): void {
        
    }
}

Hi @jaredhendrickson13 can you help me with this case, I'm trying to create this endpoint but not works with 404 Not Found error :'(

@jaredhendrickson13
Copy link
Owner

@mrizkihidayat66 once you have the endpoint class written to /usr/local/pkg/RESTAPI/Endpoints/ you'll need to run pfsense-restapi buildendponts to generate the endpoint in the pfSense web root.

You may find these resources useful for building out models and endpoints as well:

https://pfrest.org/BUILDING_CUSTOM_MODEL_CLASSES/

https://pfrest.org/BUILDING_CUSTOM_ENDPOINT_CLASSES/

@mrizkihidayat66
Copy link

@jaredhendrickson13 Hi, thank you. I can now make the API respond, but I still can't get it to return what I expect. Can you help me? I want to be able to download an OpenVPN client configuration file from the API.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backlog Issues backlogged for inclusion in future releases feature request New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants