2021-04-22 02:08:21 +00:00
import ./make-test-python.nix ( { pkgs , lib , . . . }: let
# We'll need to be able to trade cert files between nodes via scp.
inherit ( import ./ssh-keys.nix pkgs )
snakeOilPrivateKey snakeOilPublicKey ;
makeNebulaNode = { config , . . . }: name : extraConfig : lib . mkMerge [
{
# Expose nebula for doing cert signing.
environment . systemPackages = [ pkgs . nebula ] ;
users . users . root . openssh . authorizedKeys . keys = [ snakeOilPublicKey ] ;
services . openssh . enable = true ;
2024-04-21 15:54:59 +00:00
networking . firewall . enable = true ; # Implicitly true, but let's make sure.
2023-02-09 11:40:11 +00:00
networking . interfaces . eth1 . useDHCP = false ;
2021-04-22 02:08:21 +00:00
services . nebula . networks . smoke = {
# Note that these paths won't exist when the machine is first booted.
ca = " / e t c / n e b u l a / c a . c r t " ;
cert = " / e t c / n e b u l a / ${ name } . c r t " ;
key = " / e t c / n e b u l a / ${ name } . k e y " ;
2024-04-21 15:54:59 +00:00
listen = {
host = " 0 . 0 . 0 . 0 " ;
port = if ( config . services . nebula . networks . smoke . isLighthouse || config . services . nebula . networks . smoke . isRelay ) then 4242 else 0 ;
} ;
2021-04-22 02:08:21 +00:00
} ;
}
extraConfig
] ;
in
{
name = " n e b u l a " ;
nodes = {
lighthouse = { . . . } @ args :
makeNebulaNode args " l i g h t h o u s e " {
2023-02-09 11:40:11 +00:00
networking . interfaces . eth1 . ipv4 . addresses = lib . mkForce [ {
2021-04-22 02:08:21 +00:00
address = " 1 9 2 . 1 6 8 . 1 . 1 " ;
prefixLength = 24 ;
} ] ;
services . nebula . networks . smoke = {
isLighthouse = true ;
2023-02-09 11:40:11 +00:00
isRelay = true ;
2021-04-22 02:08:21 +00:00
firewall = {
outbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
inbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
} ;
} ;
} ;
2023-02-09 11:40:11 +00:00
allowAny = { . . . } @ args :
makeNebulaNode args " a l l o w A n y " {
networking . interfaces . eth1 . ipv4 . addresses = lib . mkForce [ {
2021-04-22 02:08:21 +00:00
address = " 1 9 2 . 1 6 8 . 1 . 2 " ;
prefixLength = 24 ;
} ] ;
services . nebula . networks . smoke = {
staticHostMap = { " 1 0 . 0 . 1 0 0 . 1 " = [ " 1 9 2 . 1 6 8 . 1 . 1 : 4 2 4 2 " ] ; } ;
isLighthouse = false ;
lighthouses = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2023-02-09 11:40:11 +00:00
relays = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2021-04-22 02:08:21 +00:00
firewall = {
outbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
inbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
} ;
} ;
} ;
2023-02-09 11:40:11 +00:00
allowFromLighthouse = { . . . } @ args :
makeNebulaNode args " a l l o w F r o m L i g h t h o u s e " {
networking . interfaces . eth1 . ipv4 . addresses = lib . mkForce [ {
2021-04-22 02:08:21 +00:00
address = " 1 9 2 . 1 6 8 . 1 . 3 " ;
prefixLength = 24 ;
} ] ;
services . nebula . networks . smoke = {
staticHostMap = { " 1 0 . 0 . 1 0 0 . 1 " = [ " 1 9 2 . 1 6 8 . 1 . 1 : 4 2 4 2 " ] ; } ;
isLighthouse = false ;
lighthouses = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2023-02-09 11:40:11 +00:00
relays = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2021-04-22 02:08:21 +00:00
firewall = {
outbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
inbound = [ { port = " a n y " ; proto = " a n y " ; host = " l i g h t h o u s e " ; } ] ;
} ;
} ;
} ;
2023-02-09 11:40:11 +00:00
allowToLighthouse = { . . . } @ args :
makeNebulaNode args " a l l o w T o L i g h t h o u s e " {
networking . interfaces . eth1 . ipv4 . addresses = lib . mkForce [ {
2021-04-22 02:08:21 +00:00
address = " 1 9 2 . 1 6 8 . 1 . 4 " ;
prefixLength = 24 ;
} ] ;
services . nebula . networks . smoke = {
enable = true ;
staticHostMap = { " 1 0 . 0 . 1 0 0 . 1 " = [ " 1 9 2 . 1 6 8 . 1 . 1 : 4 2 4 2 " ] ; } ;
isLighthouse = false ;
lighthouses = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2023-02-09 11:40:11 +00:00
relays = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2021-04-22 02:08:21 +00:00
firewall = {
outbound = [ { port = " a n y " ; proto = " a n y " ; host = " l i g h t h o u s e " ; } ] ;
inbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
} ;
} ;
} ;
2023-02-09 11:40:11 +00:00
disabled = { . . . } @ args :
makeNebulaNode args " d i s a b l e d " {
networking . interfaces . eth1 . ipv4 . addresses = lib . mkForce [ {
2021-04-22 02:08:21 +00:00
address = " 1 9 2 . 1 6 8 . 1 . 5 " ;
prefixLength = 24 ;
} ] ;
services . nebula . networks . smoke = {
enable = false ;
staticHostMap = { " 1 0 . 0 . 1 0 0 . 1 " = [ " 1 9 2 . 1 6 8 . 1 . 1 : 4 2 4 2 " ] ; } ;
isLighthouse = false ;
lighthouses = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2023-02-09 11:40:11 +00:00
relays = [ " 1 0 . 0 . 1 0 0 . 1 " ] ;
2021-04-22 02:08:21 +00:00
firewall = {
outbound = [ { port = " a n y " ; proto = " a n y " ; host = " l i g h t h o u s e " ; } ] ;
inbound = [ { port = " a n y " ; proto = " a n y " ; host = " a n y " ; } ] ;
} ;
} ;
} ;
} ;
testScript = let
setUpPrivateKey = name : ''
2023-02-09 11:40:11 +00:00
$ { name } . start ( )
$ { name } . succeed (
" m k d i r - p / r o o t / . s s h " ,
" c h o w n 7 0 0 / r o o t / . s s h " ,
" c a t ' ${ snakeOilPrivateKey } ' > / r o o t / . s s h / i d _ s n a k e o i l " ,
" c h o w n 6 0 0 / r o o t / . s s h / i d _ s n a k e o i l " ,
" m k d i r - p / r o o t "
)
2021-04-22 02:08:21 +00:00
'' ;
# From what I can tell, StrictHostKeyChecking=no is necessary for ssh to work between machines.
sshOpts = " - o S t r i c t H o s t K e y C h e c k i n g = n o - o U s e r K n o w n H o s t s F i l e = / d e v / n u l l - o I d e n t i t y F i l e = / r o o t / . s s h / i d _ s n a k e o i l " ;
restartAndCheckNebula = name : ip : ''
$ { name } . systemctl ( " r e s t a r t n e b u l a @ s m o k e . s e r v i c e " )
$ { name } . succeed ( " p i n g - c 5 ${ ip } " )
'' ;
# Create a keypair on the client node, then use the public key to sign a cert on the lighthouse.
signKeysFor = name : ip : ''
lighthouse . wait_for_unit ( " s s h d . s e r v i c e " )
$ { name } . wait_for_unit ( " s s h d . s e r v i c e " )
$ { name } . succeed (
" m k d i r - p / e t c / n e b u l a " ,
" n e b u l a - c e r t k e y g e n - o u t - k e y / e t c / n e b u l a / ${ name } . k e y - o u t - p u b / e t c / n e b u l a / ${ name } . p u b " ,
2023-02-09 11:40:11 +00:00
" s c p ${ sshOpts } / e t c / n e b u l a / ${ name } . p u b r o o t @ 1 9 2 . 1 6 8 . 1 . 1 : / r o o t / ${ name } . p u b " ,
2021-04-22 02:08:21 +00:00
)
lighthouse . succeed (
2023-02-09 11:40:11 +00:00
' nebula-cert sign - ca-crt /etc/nebula/ca.crt - ca-key /etc/nebula/ca.key - name " ${ name } " - groups " ${ name } " - ip " ${ ip } " - in-pub /root / $ { name } . pub - out-crt /root / $ { name } . crt'
2021-04-22 02:08:21 +00:00
)
$ { name } . succeed (
2023-02-09 11:40:11 +00:00
" s c p ${ sshOpts } r o o t @ 1 9 2 . 1 6 8 . 1 . 1 : / r o o t / ${ name } . c r t / e t c / n e b u l a / ${ name } . c r t " ,
" s c p ${ sshOpts } r o o t @ 1 9 2 . 1 6 8 . 1 . 1 : / e t c / n e b u l a / c a . c r t / e t c / n e b u l a / c a . c r t " ,
' ( id nebula-smoke > /dev/null && chown - R nebula-smoke:nebula-smoke /etc/nebula ) || true'
2021-04-22 02:08:21 +00:00
)
'' ;
2023-02-09 11:40:11 +00:00
getPublicIp = node : ''
$ { node } . succeed ( " i p - - b r i e f a d d r s h o w e t h 1 | a w k ' { p r i n t $ 3 } ' | t a i l - n 1 | c u t - d / - f 1 " ) . strip ( )
'' ;
2021-04-22 02:08:21 +00:00
2023-02-09 11:40:11 +00:00
# Never do this for anything security critical! (Thankfully it's just a test.)
# Restart Nebula right after the mutual block and/or restore so the state is fresh.
blockTrafficBetween = nodeA : nodeB : ''
node_a = $ { getPublicIp nodeA }
node_b = $ { getPublicIp nodeB }
$ { nodeA } . succeed ( " i p t a b l e s - I I N P U T - s " + node_b + " - j D R O P " )
$ { nodeB } . succeed ( " i p t a b l e s - I I N P U T - s " + node_a + " - j D R O P " )
$ { nodeA } . systemctl ( " r e s t a r t n e b u l a @ s m o k e . s e r v i c e " )
$ { nodeB } . systemctl ( " r e s t a r t n e b u l a @ s m o k e . s e r v i c e " )
'' ;
allowTrafficBetween = nodeA : nodeB : ''
node_a = $ { getPublicIp nodeA }
node_b = $ { getPublicIp nodeB }
$ { nodeA } . succeed ( " i p t a b l e s - D I N P U T - s " + node_b + " - j D R O P " )
$ { nodeB } . succeed ( " i p t a b l e s - D I N P U T - s " + node_a + " - j D R O P " )
$ { nodeA } . systemctl ( " r e s t a r t n e b u l a @ s m o k e . s e r v i c e " )
$ { nodeB } . systemctl ( " r e s t a r t n e b u l a @ s m o k e . s e r v i c e " )
'' ;
in ''
2021-04-22 02:08:21 +00:00
# Create the certificate and sign the lighthouse's keys.
$ { setUpPrivateKey " l i g h t h o u s e " }
lighthouse . succeed (
" m k d i r - p / e t c / n e b u l a " ,
' nebula-cert ca - name " S m o k e T e s t " - out-crt /etc/nebula/ca.crt - out-key /etc/nebula/ca.key ' ,
' nebula-cert sign - ca-crt /etc/nebula/ca.crt - ca-key /etc/nebula/ca.key - name " l i g h t h o u s e " - groups " l i g h t h o u s e " - ip " 1 0 . 0 . 1 0 0 . 1 / 2 4 " - out-crt /etc/nebula/lighthouse.crt - out-key /etc/nebula/lighthouse.key ' ,
2023-02-09 11:40:11 +00:00
' chown - R nebula-smoke:nebula-smoke /etc/nebula '
2021-04-22 02:08:21 +00:00
)
# Reboot the lighthouse and verify that the nebula service comes up on boot.
# Since rebooting takes a while, we'll just restart the service on the other nodes.
lighthouse . shutdown ( )
lighthouse . start ( )
lighthouse . wait_for_unit ( " n e b u l a @ s m o k e . s e r v i c e " )
lighthouse . succeed ( " p i n g - c 5 1 0 . 0 . 1 0 0 . 1 " )
2023-02-09 11:40:11 +00:00
# Create keys for allowAny's nebula service and test that it comes up.
$ { setUpPrivateKey " a l l o w A n y " }
$ { signKeysFor " a l l o w A n y " " 1 0 . 0 . 1 0 0 . 2 / 2 4 " }
$ { restartAndCheckNebula " a l l o w A n y " " 1 0 . 0 . 1 0 0 . 2 " }
2021-04-22 02:08:21 +00:00
2023-02-09 11:40:11 +00:00
# Create keys for allowFromLighthouse's nebula service and test that it comes up.
$ { setUpPrivateKey " a l l o w F r o m L i g h t h o u s e " }
$ { signKeysFor " a l l o w F r o m L i g h t h o u s e " " 1 0 . 0 . 1 0 0 . 3 / 2 4 " }
$ { restartAndCheckNebula " a l l o w F r o m L i g h t h o u s e " " 1 0 . 0 . 1 0 0 . 3 " }
2021-04-22 02:08:21 +00:00
2023-02-09 11:40:11 +00:00
# Create keys for allowToLighthouse's nebula service and test that it comes up.
$ { setUpPrivateKey " a l l o w T o L i g h t h o u s e " }
$ { signKeysFor " a l l o w T o L i g h t h o u s e " " 1 0 . 0 . 1 0 0 . 4 / 2 4 " }
$ { restartAndCheckNebula " a l l o w T o L i g h t h o u s e " " 1 0 . 0 . 1 0 0 . 4 " }
2021-04-22 02:08:21 +00:00
2023-02-09 11:40:11 +00:00
# Create keys for disabled's nebula service and test that it does not come up.
$ { setUpPrivateKey " d i s a b l e d " }
$ { signKeysFor " d i s a b l e d " " 1 0 . 0 . 1 0 0 . 5 / 2 4 " }
disabled . fail ( " s y s t e m c t l s t a t u s n e b u l a @ s m o k e . s e r v i c e " )
disabled . fail ( " p i n g - c 5 1 0 . 0 . 1 0 0 . 5 " )
2021-04-22 02:08:21 +00:00
2023-02-09 11:40:11 +00:00
# The lighthouse can ping allowAny and allowFromLighthouse but not disabled
2021-04-22 02:08:21 +00:00
lighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
lighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
lighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 5 " )
2023-02-09 11:40:11 +00:00
# allowAny can ping the lighthouse, but not allowFromLighthouse because of its inbound firewall
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 1 " )
allowAny . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
# allowFromLighthouse can ping the lighthouse and allowAny
allowFromLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 1 " )
allowFromLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
# block allowFromLighthouse <-> allowAny, and allowFromLighthouse -> allowAny should still work.
$ { blockTrafficBetween " a l l o w F r o m L i g h t h o u s e " " a l l o w A n y " }
allowFromLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
$ { allowTrafficBetween " a l l o w F r o m L i g h t h o u s e " " a l l o w A n y " }
allowFromLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
# allowToLighthouse can ping the lighthouse but not allowAny or allowFromLighthouse
allowToLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 1 " )
allowToLighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
allowToLighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
# allowAny can ping allowFromLighthouse now that allowFromLighthouse pinged it first
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
# block allowAny <-> allowFromLighthouse, and allowAny -> allowFromLighthouse should still work.
$ { blockTrafficBetween " a l l o w A n y " " a l l o w F r o m L i g h t h o u s e " }
allowFromLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
allowAny . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 3 " )
$ { allowTrafficBetween " a l l o w A n y " " a l l o w F r o m L i g h t h o u s e " }
allowFromLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
allowAny . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 3 " )
# allowToLighthouse can ping allowAny if allowAny pings it first
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 4 " )
allowToLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
# block allowToLighthouse <-> allowAny, and allowAny <-> allowToLighthouse should still work.
$ { blockTrafficBetween " a l l o w A n y " " a l l o w T o L i g h t h o u s e " }
allowAny . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 4 " )
allowToLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
$ { allowTrafficBetween " a l l o w A n y " " a l l o w T o L i g h t h o u s e " }
allowAny . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 4 " )
allowToLighthouse . succeed ( " p i n g - c 1 0 1 0 . 0 . 1 0 0 . 2 " )
# block lighthouse <-> allowFromLighthouse and allowAny <-> allowFromLighthouse; allowFromLighthouse won't get to allowAny
$ { blockTrafficBetween " a l l o w F r o m L i g h t h o u s e " " l i g h t h o u s e " }
$ { blockTrafficBetween " a l l o w F r o m L i g h t h o u s e " " a l l o w A n y " }
allowFromLighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
$ { allowTrafficBetween " a l l o w F r o m L i g h t h o u s e " " l i g h t h o u s e " }
$ { allowTrafficBetween " a l l o w F r o m L i g h t h o u s e " " a l l o w A n y " }
allowFromLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
# block lighthouse <-> allowAny, allowAny <-> allowFromLighthouse, and allowAny <-> allowToLighthouse; it won't get to allowFromLighthouse or allowToLighthouse
$ { blockTrafficBetween " a l l o w A n y " " l i g h t h o u s e " }
$ { blockTrafficBetween " a l l o w A n y " " a l l o w F r o m L i g h t h o u s e " }
$ { blockTrafficBetween " a l l o w A n y " " a l l o w T o L i g h t h o u s e " }
allowFromLighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
allowAny . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
allowAny . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 4 " )
$ { allowTrafficBetween " a l l o w A n y " " l i g h t h o u s e " }
$ { allowTrafficBetween " a l l o w A n y " " a l l o w F r o m L i g h t h o u s e " }
$ { allowTrafficBetween " a l l o w A n y " " a l l o w T o L i g h t h o u s e " }
allowFromLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 3 " )
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 4 " )
# block lighthouse <-> allowToLighthouse and allowToLighthouse <-> allowAny; it won't get to allowAny
$ { blockTrafficBetween " a l l o w T o L i g h t h o u s e " " l i g h t h o u s e " }
$ { blockTrafficBetween " a l l o w T o L i g h t h o u s e " " a l l o w A n y " }
allowAny . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 4 " )
allowToLighthouse . fail ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
$ { allowTrafficBetween " a l l o w T o L i g h t h o u s e " " l i g h t h o u s e " }
$ { allowTrafficBetween " a l l o w T o L i g h t h o u s e " " a l l o w A n y " }
allowAny . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 4 " )
allowToLighthouse . succeed ( " p i n g - c 3 1 0 . 0 . 1 0 0 . 2 " )
2021-04-22 02:08:21 +00:00
'' ;
} )