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

Comprehensive remote GPIO documentation #434

Closed
bennuttall opened this Issue Sep 13, 2016 · 18 comments

Comments

Projects
None yet
6 participants
@bennuttall
Member

bennuttall commented Sep 13, 2016

The improvements in remote GPIO support have been fantastic. We just need to make sure we have solid instructions for getting everything set up if we want to see it used in schools, code clubs and such.

This means:

  • How to install gpiozero on Windows / Mac / Linux
  • How to install the pigpio Python library on Windows / Mac / Linux
  • How to enable remote GPIO on the Pi in raspi-config
  • How to run the daemon on the Pi (auto run on boot would be handy)
  • Different ways of how to use it (pin objects, default pin factory, mixed, etc.)

Update: We should also add a "remote gpio recipes" page.

@lurch

This comment has been minimized.

Show comment
Hide comment
@lurch

lurch Sep 14, 2016

Contributor

@joan2937 If pipgpio is being used in 'remote' (i.e. gpiozero on a PC being used to control the gpio pins on a pi over the network) mode in a school or code-club environment, is there any mechanism to stop one group of kids (accidentally or maliciously) controlling the GPIOs/LEDs on another group's Raspberry Pi?
(assuming they're all connected to the same Ethernet network, and we're not using USB OTG or anything)
Can two PC-clients end up controlling the same pigpiod-server?

Contributor

lurch commented Sep 14, 2016

@joan2937 If pipgpio is being used in 'remote' (i.e. gpiozero on a PC being used to control the gpio pins on a pi over the network) mode in a school or code-club environment, is there any mechanism to stop one group of kids (accidentally or maliciously) controlling the GPIOs/LEDs on another group's Raspberry Pi?
(assuming they're all connected to the same Ethernet network, and we're not using USB OTG or anything)
Can two PC-clients end up controlling the same pigpiod-server?

@bennuttall

This comment has been minimized.

Show comment
Hide comment
@bennuttall

bennuttall Sep 14, 2016

Member

I think that is a potential issue. Accidental or otherwise, remote GPIO could be funny/annoying/disruptive/dangerous.

I think there are options for keeping this more secure, perhaps using port-forwarding instead of just opening up remote GPIO access?

Member

bennuttall commented Sep 14, 2016

I think that is a potential issue. Accidental or otherwise, remote GPIO could be funny/annoying/disruptive/dangerous.

I think there are options for keeping this more secure, perhaps using port-forwarding instead of just opening up remote GPIO access?

@joan2937

This comment has been minimized.

Show comment
Hide comment
@joan2937

joan2937 Sep 14, 2016

At the moment remote pigpio access is all or nothing.

It would be straightforward to add an option to the pigpio daemon to limit network connections to defined addresses, e.g. something along the lines

sudo pigpiod -npaul -ngavin -nlocalhost

could be used to allow access to that Pi's GPIO from the local Pi, and from machines paul and gavin.

If no -n options were defined access would be open as at the moment.

Would that be suitable?

joan2937 commented Sep 14, 2016

At the moment remote pigpio access is all or nothing.

It would be straightforward to add an option to the pigpio daemon to limit network connections to defined addresses, e.g. something along the lines

sudo pigpiod -npaul -ngavin -nlocalhost

could be used to allow access to that Pi's GPIO from the local Pi, and from machines paul and gavin.

If no -n options were defined access would be open as at the moment.

Would that be suitable?

@lurch

This comment has been minimized.

Show comment
Hide comment
@lurch

lurch Sep 15, 2016

Contributor

@joan2937 would that work with bare IP-addresses too? I suspect that many places don't bother with full DNS resolution for all their RPis ;)

(and, although very unlikely, could the source IP that a pigpio packet comes from technically be spoofed?)

Contributor

lurch commented Sep 15, 2016

@joan2937 would that work with bare IP-addresses too? I suspect that many places don't bother with full DNS resolution for all their RPis ;)

(and, although very unlikely, could the source IP that a pigpio packet comes from technically be spoofed?)

@dglaude

This comment has been minimized.

Show comment
Hide comment
@dglaude

dglaude Sep 15, 2016

Contributor

Wrote this yesterday but forgot to send:

Of course exposing your GPIO with pigpiod open to all is a risk.
AFAIK, that protocol as no authentification nor encryption and so it is not
design for it.

The easy way is to have USB OTG and NAT so that only the host sharing it's
connectivity can access the deamon via the local link.
So that is a GPIO extension of the host and the host only. Same can be made
with a back to back ethernet or wifi link

One way to limit the exposure is to only accept local connection (listening
to 127.0.0.1).
Then to open access to the chosen port can be done using ssh port
forwarding.
This mean opening an ssh connection for the computer running GPIO Zero and
the Pi running pigpiod, but adding a mapping between a local port and a
remote port.
This is not really "easy" to explain in a school, even it is a very
important skill and knowledge that might be used by Mr Robot.

I would advice not to change the current protocole, at least it should not
be GpioZero job to change that.

One non intrusive way to add security would be to build an
SSL/authentication warper on top of the named pipe version of pigpiod. But
this might be overkill.

On 14 Sep 2016 02:11, "Ben Nuttall" notifications@github.com wrote:

I think that is a potential issue. Accidental or otherwise, remote GPIO
could be funny/annoying/disruptive/dangerous.

I think there are options for keeping this more secure, perhaps using
port-forwarding instead of just opening up remote GPIO access?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#434 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ASiRnELqEMeZ32jxrPLqjoIIlVnt66Zhks5qpzu1gaJpZM4J8Qfj
.

Contributor

dglaude commented Sep 15, 2016

Wrote this yesterday but forgot to send:

Of course exposing your GPIO with pigpiod open to all is a risk.
AFAIK, that protocol as no authentification nor encryption and so it is not
design for it.

The easy way is to have USB OTG and NAT so that only the host sharing it's
connectivity can access the deamon via the local link.
So that is a GPIO extension of the host and the host only. Same can be made
with a back to back ethernet or wifi link

One way to limit the exposure is to only accept local connection (listening
to 127.0.0.1).
Then to open access to the chosen port can be done using ssh port
forwarding.
This mean opening an ssh connection for the computer running GPIO Zero and
the Pi running pigpiod, but adding a mapping between a local port and a
remote port.
This is not really "easy" to explain in a school, even it is a very
important skill and knowledge that might be used by Mr Robot.

I would advice not to change the current protocole, at least it should not
be GpioZero job to change that.

One non intrusive way to add security would be to build an
SSL/authentication warper on top of the named pipe version of pigpiod. But
this might be overkill.

On 14 Sep 2016 02:11, "Ben Nuttall" notifications@github.com wrote:

I think that is a potential issue. Accidental or otherwise, remote GPIO
could be funny/annoying/disruptive/dangerous.

I think there are options for keeping this more secure, perhaps using
port-forwarding instead of just opening up remote GPIO access?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#434 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/ASiRnELqEMeZ32jxrPLqjoIIlVnt66Zhks5qpzu1gaJpZM4J8Qfj
.

@joan2937

This comment has been minimized.

Show comment
Hide comment
@joan2937

joan2937 Sep 15, 2016

@lurch

Yes, I would use getaddrinfo to translate the identifier to an IP address, which works for host names and dotted number notation.

static uint32_t checkAddr(char *addrStr)
{
   int err;
   struct addrinfo hints, *res;
   struct sockaddr_in *sin;
   const char *portStr;
   uint32_t addr;

   portStr = getenv(PI_ENVPORT);

   if (!portStr) portStr = PI_DEFAULT_SOCKET_PORT_STR;

   memset (&hints, 0, sizeof (hints));

   hints.ai_family   = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags   |= AI_CANONNAME;

   err = getaddrinfo(addrStr, portStr, &hints, &res);

   if (err) return 0;

   sin = (struct sockaddr_in *)res->ai_addr;
   printf("a=%s addr=%x\n", addrStr, sin->sin_addr.s_addr);
   addr = sin->sin_addr.s_addr;

   freeaddrinfo(res);

   return addr;
}

"(and, although very unlikely, could the source IP that a pigpio packet comes from technically be spoofed?)"

I have no idea.

@dglaude

I'm afraid ssh is beyond my experience apart from as a user.

I'd have thought the simple network name based access control would be sufficient for most uses. I don't have the knowledge to be able to make the daemon "secure".

joan2937 commented Sep 15, 2016

@lurch

Yes, I would use getaddrinfo to translate the identifier to an IP address, which works for host names and dotted number notation.

static uint32_t checkAddr(char *addrStr)
{
   int err;
   struct addrinfo hints, *res;
   struct sockaddr_in *sin;
   const char *portStr;
   uint32_t addr;

   portStr = getenv(PI_ENVPORT);

   if (!portStr) portStr = PI_DEFAULT_SOCKET_PORT_STR;

   memset (&hints, 0, sizeof (hints));

   hints.ai_family   = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags   |= AI_CANONNAME;

   err = getaddrinfo(addrStr, portStr, &hints, &res);

   if (err) return 0;

   sin = (struct sockaddr_in *)res->ai_addr;
   printf("a=%s addr=%x\n", addrStr, sin->sin_addr.s_addr);
   addr = sin->sin_addr.s_addr;

   freeaddrinfo(res);

   return addr;
}

"(and, although very unlikely, could the source IP that a pigpio packet comes from technically be spoofed?)"

I have no idea.

@dglaude

I'm afraid ssh is beyond my experience apart from as a user.

I'd have thought the simple network name based access control would be sufficient for most uses. I don't have the knowledge to be able to make the daemon "secure".

@lurch

This comment has been minimized.

Show comment
Hide comment
@lurch

lurch Sep 15, 2016

Contributor

@joan2937 The network name (or IP address) based access control sounds like a good idea.
Thinking about what other daemons do, would it be possible to (optionally) restrict the IP addresses that pigpiod listens on, so that for example if used on a Pi3 which has both active Ethernet and Wifi interfaces, you could tell pigpiod to only respond to requests coming in over Ethernet?

As @dglaude points out, if 'stronger' security is required, that can be accomplished by the user (without having to modify pigpio) by tunnelling over SSH.

Contributor

lurch commented Sep 15, 2016

@joan2937 The network name (or IP address) based access control sounds like a good idea.
Thinking about what other daemons do, would it be possible to (optionally) restrict the IP addresses that pigpiod listens on, so that for example if used on a Pi3 which has both active Ethernet and Wifi interfaces, you could tell pigpiod to only respond to requests coming in over Ethernet?

As @dglaude points out, if 'stronger' security is required, that can be accomplished by the user (without having to modify pigpio) by tunnelling over SSH.

@joan2937

This comment has been minimized.

Show comment
Hide comment
@joan2937

joan2937 Sep 15, 2016

@lurch At this stage I'm reluctant to do more than the bare minimum to meet reasonable security. There is always the risk of breakage.

joan2937 commented Sep 15, 2016

@lurch At this stage I'm reluctant to do more than the bare minimum to meet reasonable security. There is always the risk of breakage.

@WayneKeenan

This comment has been minimized.

Show comment
Hide comment
@WayneKeenan

WayneKeenan Sep 15, 2016

Why re-implement a subset of what xinetd can already do, and more, for you?

google: Access Control Using xinetd

WayneKeenan commented Sep 15, 2016

Why re-implement a subset of what xinetd can already do, and more, for you?

google: Access Control Using xinetd

@lurch

This comment has been minimized.

Show comment
Hide comment
@lurch

lurch Sep 15, 2016

Contributor

Why re-implement a subset of what xinetd can already do, and more, for you?

Probably because nobody suggested it until now? ;-)

google: Access Control Using xinetd

I had a quick look. Is it compatible with the systemd-stuff now used by Raspbian? (I've got no idea how inetd / xinetd / systemd all inter-relate, if at all)

Contributor

lurch commented Sep 15, 2016

Why re-implement a subset of what xinetd can already do, and more, for you?

Probably because nobody suggested it until now? ;-)

google: Access Control Using xinetd

I had a quick look. Is it compatible with the systemd-stuff now used by Raspbian? (I've got no idea how inetd / xinetd / systemd all inter-relate, if at all)

@WayneKeenan

This comment has been minimized.

Show comment
Hide comment
@WayneKeenan

WayneKeenan Sep 15, 2016

xinetd is an improved inetd
It's available as a package for Raspbian which comes with 'older' init scripts that, on the surface, seems to be compatible with systemd.

Running system's systemctl shows:

xinetd.service        loaded active running   LSB: Starts or stops the xinetd daemon.

A standard xinetd config for pipgpio would need creating, with a default ACL of only allowing localhost TCP source addresses to talk to the pipgpio daemon.

Basic TCP Wrapper instructions, perhaps just a few lines of a template, on how to add a IP/Hostname whitelist would need adding to the docs.

WayneKeenan commented Sep 15, 2016

xinetd is an improved inetd
It's available as a package for Raspbian which comes with 'older' init scripts that, on the surface, seems to be compatible with systemd.

Running system's systemctl shows:

xinetd.service        loaded active running   LSB: Starts or stops the xinetd daemon.

A standard xinetd config for pipgpio would need creating, with a default ACL of only allowing localhost TCP source addresses to talk to the pipgpio daemon.

Basic TCP Wrapper instructions, perhaps just a few lines of a template, on how to add a IP/Hostname whitelist would need adding to the docs.

@joan2937

This comment has been minimized.

Show comment
Hide comment
@joan2937

joan2937 Sep 15, 2016

@lurch @WayneKeenan

I'll leave the best way of doing things down to you guys.

I'll make the changes to the daemon that I have outlined. It may provide a stop-gap solution if nothing else.

joan2937 commented Sep 15, 2016

@lurch @WayneKeenan

I'll leave the best way of doing things down to you guys.

I'll make the changes to the daemon that I have outlined. It may provide a stop-gap solution if nothing else.

@WayneKeenan

This comment has been minimized.

Show comment
Hide comment
@WayneKeenan

WayneKeenan Sep 15, 2016

I just took a stab at setting up xinet with pipgio but ran into a blocker...
pigpio would need to support reading and writing to and from stdin and stdout, rather than use the Sockets API directly, to work with xinetd.

Should that ever ever change, or on the chance other gpio daemons can operate using stdio/stdin... here was the config:

Install:

sudo apt-get install xinetd

file: /etc/xinetd.conf

defaults
{
        instances             = 1
        log_type               = SYSLOG authpriv
        log_on_success    = HOST PID
        log_on_failure        = HOST
}

includedir /etc/xinetd.d

file: /etc/xinetd.d/pigpio

service pigpio
{
        only_from       = localhost
        port            = 8888
        flags           = REUSE
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/bin/pigpiod
        log_on_failure  += USERID
        disable         = no
}

Add pigpio to /etc/services:

echo 'pigpio 8888/tcp' | sudo tee --append /etc/services > /dev/null

Reload config:

sudo systemctl reload xinetd.service

WayneKeenan commented Sep 15, 2016

I just took a stab at setting up xinet with pipgio but ran into a blocker...
pigpio would need to support reading and writing to and from stdin and stdout, rather than use the Sockets API directly, to work with xinetd.

Should that ever ever change, or on the chance other gpio daemons can operate using stdio/stdin... here was the config:

Install:

sudo apt-get install xinetd

file: /etc/xinetd.conf

defaults
{
        instances             = 1
        log_type               = SYSLOG authpriv
        log_on_success    = HOST PID
        log_on_failure        = HOST
}

includedir /etc/xinetd.d

file: /etc/xinetd.d/pigpio

service pigpio
{
        only_from       = localhost
        port            = 8888
        flags           = REUSE
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/bin/pigpiod
        log_on_failure  += USERID
        disable         = no
}

Add pigpio to /etc/services:

echo 'pigpio 8888/tcp' | sudo tee --append /etc/services > /dev/null

Reload config:

sudo systemctl reload xinetd.service
@joan2937

This comment has been minimized.

Show comment
Hide comment
@joan2937

joan2937 Sep 22, 2016

pigpio V56 incorporates the network permission changes. It's the -n option to pigpiod (man pigpiod).

sudo pigpiod # allow all
sudo pigpiod -n localhost # allow localhost only
sudo pigpiod -n 192.168.1.65 # allow 192.168.1.65 only
sudo pigpiod -n localhost -n 192.168.1.65 # allow localhost and 192.168.1.65 only

joan2937 commented Sep 22, 2016

pigpio V56 incorporates the network permission changes. It's the -n option to pigpiod (man pigpiod).

sudo pigpiod # allow all
sudo pigpiod -n localhost # allow localhost only
sudo pigpiod -n 192.168.1.65 # allow 192.168.1.65 only
sudo pigpiod -n localhost -n 192.168.1.65 # allow localhost and 192.168.1.65 only
@waveform80

This comment has been minimized.

Show comment
Hide comment
@waveform80

waveform80 Sep 22, 2016

Member

Sorry, I probably should've weighed in on this a bit sooner - if you want to limit by IP you can do so for any service via iptables. No blockers as with xinetd; iptables operates at the kernel level and can quite easily block (or permit) based on IP address (along with a host of other things). It's more complex to setup than a simple command line switch (especially if mixed with other iptables rules) and given that's now available in pigpiod it's probably not worth persuing at this point.

Just as an example for anyone curious, though, here's how you'd add rules to the INPUT chain in the filter table to prevent connections to local TCP port 8888 (pigpiod's default) from addresses other than those permitted (local, 192.168.1.65 & .66, along with everything on the 192.168.2.0 subnet):

$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state ESTABLISHED -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 127.0.0.0/8 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.1.65 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.1.66 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.2.0/24 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -j DROP

As for spoofing ... I'm no expert in this area (merely a dabbler) but given this is a TCP based protocol I'd assume ARP poisoning or MITM would be required if two-way communication were needed at any point (I haven't looked at pigpiod's protocol in depth yet so I've no idea if this is required). If two-way communication isn't required it's probably as trivial as it is with UDP.

On the subject of securing it "properly", SSH port forwarding is almost certainly the easiest (and most secure) route. Pi's have an SSH server as standard, so no configuration required there. The configuration on the client is also pretty trivial: forward a local port (e.g. 8888) to the remote's localhost:8888 (assuming pigpiod is only listening on the localhost interface, which is the default). With the SSH command line this would simply be:

$ ssh -L 8888:localhost:8888 pi@raspberrypi

Once the session was established you could then use pigpiod on the client machine (from another terminal, obviously) as if it were on the Pi and all communication would be strongly encrypted over the SSH tunnel. This would also be useful for securing on a per-user basis (via public key or password credentials), something that iptables has difficulty with as it's operating below the application protocol layer.

Member

waveform80 commented Sep 22, 2016

Sorry, I probably should've weighed in on this a bit sooner - if you want to limit by IP you can do so for any service via iptables. No blockers as with xinetd; iptables operates at the kernel level and can quite easily block (or permit) based on IP address (along with a host of other things). It's more complex to setup than a simple command line switch (especially if mixed with other iptables rules) and given that's now available in pigpiod it's probably not worth persuing at this point.

Just as an example for anyone curious, though, here's how you'd add rules to the INPUT chain in the filter table to prevent connections to local TCP port 8888 (pigpiod's default) from addresses other than those permitted (local, 192.168.1.65 & .66, along with everything on the 192.168.2.0 subnet):

$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state ESTABLISHED -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 127.0.0.0/8 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.1.65 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.1.66 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -m state --state NEW -s 192.168.2.0/24 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8888 -j DROP

As for spoofing ... I'm no expert in this area (merely a dabbler) but given this is a TCP based protocol I'd assume ARP poisoning or MITM would be required if two-way communication were needed at any point (I haven't looked at pigpiod's protocol in depth yet so I've no idea if this is required). If two-way communication isn't required it's probably as trivial as it is with UDP.

On the subject of securing it "properly", SSH port forwarding is almost certainly the easiest (and most secure) route. Pi's have an SSH server as standard, so no configuration required there. The configuration on the client is also pretty trivial: forward a local port (e.g. 8888) to the remote's localhost:8888 (assuming pigpiod is only listening on the localhost interface, which is the default). With the SSH command line this would simply be:

$ ssh -L 8888:localhost:8888 pi@raspberrypi

Once the session was established you could then use pigpiod on the client machine (from another terminal, obviously) as if it were on the Pi and all communication would be strongly encrypted over the SSH tunnel. This would also be useful for securing on a per-user basis (via public key or password credentials), something that iptables has difficulty with as it's operating below the application protocol layer.

@waveform80

This comment has been minimized.

Show comment
Hide comment
@waveform80

waveform80 Sep 22, 2016

Member

Oh, incidentally, don't go adding anything to docs just yet - as I mentioned in #459 I'm slightly tweaking what's specified for GPIOZERO_PIN_FACTORY as part of that so it's probably better to hold off on advertising this until that lands (should be tomorrow-ish - I finally got a decent pin conflict mechanism implemented today!)

Member

waveform80 commented Sep 22, 2016

Oh, incidentally, don't go adding anything to docs just yet - as I mentioned in #459 I'm slightly tweaking what's specified for GPIOZERO_PIN_FACTORY as part of that so it's probably better to hold off on advertising this until that lands (should be tomorrow-ish - I finally got a decent pin conflict mechanism implemented today!)

@SrMouraSilva SrMouraSilva referenced this issue Sep 25, 2016

Open

Add RotaryEncoder support #1

14 of 14 tasks complete
@bennuttall

This comment has been minimized.

Show comment
Hide comment
@bennuttall
Member

bennuttall commented Jan 9, 2017

@bennuttall bennuttall self-assigned this Jan 9, 2017

@bennuttall

This comment has been minimized.

Show comment
Hide comment
@bennuttall

bennuttall Jan 9, 2017

Member

I'm now working on this

Member

bennuttall commented Jan 9, 2017

I'm now working on this

@bennuttall bennuttall added this to the v1.4 milestone Jan 9, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment