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

Bring your own public key #303

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions internal/devices/devices.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package devices
import (
"fmt"
"net/netip"
"regexp"
"sync"
"time"

Expand All @@ -26,6 +27,9 @@ type User struct {
Name string
}

// https://lists.zx2c4.com/pipermail/wireguard/2020-December/006222.html
var regex = regexp.MustCompile("^[A-Za-z0-9+/]{42}[A|E|I|M|Q|U|Y|c|g|k|o|s|w|4|8|0]=$")

func New(wg wgembed.WireGuardInterface, s storage.Storage, cidr, cidrv6 string) *DeviceManager {
return &DeviceManager{wg, s, cidr, cidrv6}
}
Expand Down Expand Up @@ -90,6 +94,10 @@ func (d *DeviceManager) AddDevice(identity *authsession.Identity, name string, p
return nil, errors.New("device name already taken")
}

if !regex.MatchString(publicKey) {
return nil, errors.New("public key has invalid format")
}

clientAddr, err := d.nextClientAddress()
if err != nil {
return nil, errors.Wrap(err, "failed to generate an ip address for device")
Expand Down
12 changes: 6 additions & 6 deletions internal/services/device_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package services
import (
"context"

"github.com/freifunkMUC/wg-access-server/internal/devices"
"github.com/freifunkMUC/wg-access-server/internal/storage"
"github.com/freifunkMUC/wg-access-server/pkg/authnz/authsession"
"github.com/freifunkMUC/wg-access-server/proto/proto"

"github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus/ctxlogrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"

"github.com/freifunkMUC/wg-access-server/internal/devices"
"github.com/freifunkMUC/wg-access-server/internal/storage"
"github.com/freifunkMUC/wg-access-server/pkg/authnz/authsession"
"github.com/freifunkMUC/wg-access-server/proto/proto"
)

type DeviceService struct {
Expand All @@ -28,7 +28,7 @@ func (d *DeviceService) AddDevice(ctx context.Context, req *proto.AddDeviceReq)
device, err := d.DeviceManager.AddDevice(user, req.GetName(), req.GetPublicKey())
if err != nil {
ctxlogrus.Extract(ctx).Error(err)
return nil, status.Errorf(codes.Internal, "failed to add device")
return nil, status.Errorf(codes.Internal, err.Error())
}

return mapDevice(device), nil
Expand Down
43 changes: 35 additions & 8 deletions website/src/components/AddDevice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import InputLabel from '@material-ui/core/InputLabel';
import Typography from '@material-ui/core/Typography';
import AddIcon from '@material-ui/icons/Add';
import { codeBlock } from 'common-tags';
import { observable, makeObservable } from 'mobx';
import { makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import React from 'react';
import { box_keyPair } from 'tweetnacl-ts';
Expand All @@ -33,14 +33,27 @@ export const AddDevice = observer(class AddDevice extends React.Component<Props>

deviceName = '';

devicePublickey = '';

configFile?: string;

showMobile = true;

submit = async (event: React.FormEvent) => {
event.preventDefault();

const keypair = box_keyPair();
const publicKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.publicKey) as any)));
const privateKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.secretKey) as any)));
var publicKey: string;
var privateKey: string;
if (this.devicePublickey) {
publicKey = this.devicePublickey
privateKey = 'pleaseReplaceThisPrivatekey'
this.showMobile = false;
} else {
publicKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.publicKey) as any)));
privateKey = window.btoa(String.fromCharCode(...(new Uint8Array(keypair.secretKey) as any)));
this.showMobile = true;
}

try {
const device = await grpc.devices.addDevice({
Expand Down Expand Up @@ -68,12 +81,14 @@ export const AddDevice = observer(class AddDevice extends React.Component<Props>
} catch (error) {
console.log(error);
// TODO: unwrap grpc error message
this.error = 'failed';
this.error = 'failed to add device';;
}
};

reset = () => {
this.deviceName = '';
this.devicePublickey = '';
this.error = '';
};

constructor(props: Props) {
Expand All @@ -83,7 +98,9 @@ export const AddDevice = observer(class AddDevice extends React.Component<Props>
dialogOpen: observable,
error: observable,
deviceName: observable,
configFile: observable
devicePublickey: observable,
configFile: observable,
showMobile: observable
});
}

Expand All @@ -94,16 +111,26 @@ export const AddDevice = observer(class AddDevice extends React.Component<Props>
<CardHeader title="Add A Device" />
<CardContent>
<form onSubmit={this.submit}>
<FormControl error={!!this.error} fullWidth>
<FormControl fullWidth>
<InputLabel htmlFor="device-name">Device Name</InputLabel>
<Input
id="device-name"
value={this.deviceName}
onChange={(event) => (this.deviceName = event.currentTarget.value)}
aria-describedby="device-name-text"
/>
<FormHelperText id="device-name-text">{this.error}</FormHelperText>
</FormControl>
<FormControl fullWidth>
<InputLabel htmlFor="device-publickey">Device Public Key (Optional)</InputLabel>
<Input
id="device-publickey"
value={this.devicePublickey}
onChange={(event) => (this.devicePublickey = event.currentTarget.value)}
aria-describedby="device-publickey-text"
/>
<FormHelperText id="device-publickey-text">Put your public key to a pre-generated private key here. Replace the private key in the config file after downloading it.</FormHelperText>
</FormControl>
<FormHelperText id="device-error-text" error={true}>{this.error}</FormHelperText>
<Typography component="div" align="right">
<Button color="secondary" type="button" onClick={this.reset}>
Cancel
Expand Down Expand Up @@ -133,7 +160,7 @@ export const AddDevice = observer(class AddDevice extends React.Component<Props>
</Info>
</DialogTitle>
<DialogContent>
<GetConnected configFile={this.configFile!} />
<GetConnected configFile={this.configFile!} showMobile={this.showMobile}/>
</DialogContent>
<DialogActions>
<Button color="secondary" variant="outlined" onClick={() => (this.dialogOpen = false)}>
Expand Down
7 changes: 6 additions & 1 deletion website/src/components/GetConnected.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import { TabPanel } from './TabPanel';

interface Props {
configFile: string;
showMobile: boolean;
}

export class GetConnected extends React.Component<Props> {
state = {
currentTab: isMobile() ? 'mobile' : 'desktop',
currentTab: isMobile() && this.props.showMobile ? 'mobile' : 'desktop',
};

go = (href: string) => {
Expand Down Expand Up @@ -55,7 +56,9 @@ export class GetConnected extends React.Component<Props> {
variant="fullWidth"
>
<Tab icon={<Laptop />} value="desktop" />
{this.props.showMobile &&
<Tab icon={<PhoneIphone />} value="mobile" />
}
</Tabs>
</Paper>

Expand Down Expand Up @@ -91,6 +94,7 @@ export class GetConnected extends React.Component<Props> {
</Grid>
</TabPanel>

{this.props.showMobile &&
<TabPanel for="mobile" value={this.state.currentTab}>
<Grid container direction="row" justify="space-around" alignItems="center">
<Grid item>
Expand All @@ -111,6 +115,7 @@ export class GetConnected extends React.Component<Props> {
</Grid>
</Grid>
</TabPanel>
}
</React.Fragment>
);
}
Expand Down