Compare commits

...

49 Commits

Author SHA1 Message Date
Brian Picciano 249c46c586 Update docs for obtaining a binary 5 months ago
Brian Picciano 3d02be1be0 Year-end roadmap review 5 months ago
Brian Picciano a7429bd176 Update testing notest 6 months ago
Brian Picciano 745f7786e8 Update roadmap, gateway doc is no longer necessary 9 months ago
Brian Picciano 5ee80b1b7d Add tests for DNS 9 months ago
Brian Picciano 96a3ecfe14 Perform garage tests on each of the running nodes 9 months ago
Brian Picciano 8dcc436aaa Refactor tests some more, use shared across all of them 9 months ago
Brian Picciano ceab16d05f move admin tests into their own subdir 9 months ago
Brian Picciano 3c3bd8649a Fix minio-client creating config directory in user's home 9 months ago
Brian Picciano 98e5f4c98c Add create-bootstrap test case 9 months ago
Brian Picciano 0a482607d5 Move temp directory creation into test/utils 9 months ago
Brian Picciano 73db21f841 add more checks to 01-create-network test 9 months ago
Brian Picciano 56f38ad451 Refactor how tests are organized 9 months ago
Brian Picciano a77617ae96 Add test for network creation 9 months ago
Brian Picciano ae70278a9f Add --keep-tmp flag to test script 9 months ago
Brian Picciano 0b486d5d27 Allow setting tun name in daemon config 9 months ago
Brian Picciano d2d25d3621 Set XDG variables on a per-test basis 9 months ago
Brian Picciano bc798acffa Have tests each create a separate tmpdir, and cd into it 9 months ago
Brian Picciano 48675ee095 Fix output of error logs in verbose testing 9 months ago
Brian Picciano aa0d489e88 Add verbose flag to test shell script 9 months ago
Brian Picciano 2876b56afb Fix how revision is embedded so that AppImage isn't always recompiled 9 months ago
Brian Picciano e66f67da4a Super basic testing framework 9 months ago
Brian Picciano 3d6ed8604a Add ability to sign nebula public keys, and show nebula network info 9 months ago
Brian Picciano 661e2b28cb Move go code into 'go' tld, to make organization clearer 9 months ago
Brian Picciano b5e8ad274e Add AGPLv3 license 9 months ago
Brian Picciano 633c7147b1 Fix create host docs 9 months ago
Brian Picciano 6480f6c843 Fix global shared bucket name to conform to S3 naming standards 9 months ago
Brian Picciano fafd711b1b Fix doc generation, remove cryptic references from plantuml 9 months ago
Brian Picciano 9fa32749b9 Remove old cryptic references completely, fuck it 9 months ago
Brian Picciano b7fb1d9c0a Complete in-code changes required by rename 10 months ago
Brian Picciano 3d7651208f Perform all in-code renames which don't affect actual functionality 10 months ago
Brian Picciano 2b9601f031 Renaming in AppDir and documentation 10 months ago
Brian Picciano 4e3847ea84 cryptic-io org has been renamed to micropelago 11 months ago
Brian Picciano 257b961459 Gateway doc 1 year ago
Brian Picciano a1b3ff71b3 Use entrypoint directly as AppRun 1 year ago
Brian Picciano 57f63750f3 Fix appimagetool build 1 year ago
Brian Picciano 1180540ce3 Make sure we can use nix cache for non-cross-compiling 1 year ago
Brian Picciano 3a3bd56295 Implement release script 1 year ago
Brian Picciano e9190e4dbb Allow injecting bootstrap again, plus some additions to version 1 year ago
Brian Picciano 94c6ad8774 Fix up some TODOs 1 year ago
Brian Picciano 8e800951a6 Remove sources from flake, everything is defined in default.nix now 1 year ago
Brian Picciano b7d49bff5b Allow building from either flake or nix-build 1 year ago
Brian Picciano 1354c96ba9 Accidentally left flake building garage rather than full appimage 1 year ago
Brian Picciano a8856fba99 Update docs a bit in light of the new architectures 1 year ago
Brian Picciano 1379291c1e Got flake set up for cross-compilation, but it still doesn't work 1 year ago
Brian Picciano 05f9064d10 Update nixpkgs, add appimagetool for other archs 1 year ago
Brian Picciano 5061fb5670 Update appimagetool-ing to something which can theoretically be used on other architectures 1 year ago
Brian Picciano 17fb9bbd77 Add a flake.nix 1 year ago
Brian Picciano 1dc22701cd Write rpc_port file to garage meta dir 1 year ago
  1. 4
      AppDir/AppRun
  2. 4
      AppDir/Isle.desktop
  3. 10
      AppDir/etc/daemon.yml
  4. 661
      LICENSE.txt
  5. 25
      README.md
  6. 183
      default.nix
  7. 18
      docs/admin/adding-a-host-to-the-network.md
  8. 21
      docs/admin/creating-a-new-network.md
  9. 3
      docs/dev/daemon-process-tree.plantuml
  10. 18
      docs/dev/daemon-process-tree.svg
  11. 13
      docs/dev/design-principles.md
  12. 2
      docs/dev/rebuilding-documentation.md
  13. 32
      docs/dev/releases.md
  14. 19
      docs/glossary.md
  15. 8
      docs/operator/contributing-a-lighthouse.md
  16. 18
      docs/operator/contributing-storage.md
  17. 17
      docs/operator/firewalls.md
  18. 22
      docs/operator/managing-garage.md
  19. 134
      docs/roadmap.md
  20. 28
      docs/shell.nix
  21. 10
      docs/user/creating-a-daemonyml-file.md
  22. 68
      docs/user/getting-started.md
  23. 10
      docs/user/using-dns.md
  24. 14
      entrypoint/default.nix
  25. 105
      entrypoint/src/garage/garage.go
  26. 7
      flake.lock
  27. 36
      flake.nix
  28. 4
      go/admin/admin.go
  29. 6
      go/bootstrap/bootstrap.go
  30. 2
      go/bootstrap/garage.go
  31. 4
      go/bootstrap/garage_global_bucket.go
  32. 2
      go/bootstrap/hosts.go
  33. 90
      go/cmd/entrypoint/admin.go
  34. 7
      go/cmd/entrypoint/bootstrap_util.go
  35. 16
      go/cmd/entrypoint/daemon.go
  36. 24
      go/cmd/entrypoint/daemon_util.go
  37. 10
      go/cmd/entrypoint/dnsmasq_util.go
  38. 44
      go/cmd/entrypoint/garage.go
  39. 12
      go/cmd/entrypoint/garage_util.go
  40. 2
      go/cmd/entrypoint/hosts.go
  41. 0
      go/cmd/entrypoint/logger.go
  42. 11
      go/cmd/entrypoint/main.go
  43. 82
      go/cmd/entrypoint/nebula.go
  44. 14
      go/cmd/entrypoint/nebula_util.go
  45. 4
      go/cmd/entrypoint/proc_lock.go
  46. 0
      go/cmd/entrypoint/sub_cmd.go
  47. 0
      go/cmd/entrypoint/version.go
  48. 5
      go/daemon/config.go
  49. 4
      go/daemon/daemon.go
  50. 0
      go/dnsmasq/dnsmasq.go
  51. 0
      go/dnsmasq/tpl.go
  52. 0
      go/garage/admin_client.go
  53. 0
      go/garage/client.go
  54. 153
      go/garage/garage.go
  55. 0
      go/garage/peer.go
  56. 0
      go/garage/tpl.go
  57. 4
      go/go.mod
  58. 4
      go/go.sum
  59. 97
      go/nebula/nebula.go
  60. 0
      go/nebula/nebula_test.go
  61. 0
      go/yamlutil/yamlutil.go
  62. 47
      nix/appimagetool.nix
  63. 5
      nix/dnsmasq.nix
  64. 44
      nix/garage.nix
  65. BIN
      nix/go-appimage/appimagetool-765-aarch64.AppImage
  66. BIN
      nix/go-appimage/appimagetool-765-armhf.AppImage
  67. BIN
      nix/go-appimage/appimagetool-765-i686.AppImage
  68. BIN
      nix/go-appimage/appimagetool-765-x86_64.AppImage
  69. 72
      nix/pkgs.nix
  70. 51
      release.nix
  71. 37
      release.sh
  72. 16
      tests.sh
  73. 10
      tests/NOTES.txt
  74. 3
      tests/cases/00-version.sh
  75. 16
      tests/cases/admin/01-create-network.sh
  76. 14
      tests/cases/admin/02-create-bootstrap.sh
  77. 20
      tests/cases/dnsmasq/00-hosts.sh
  78. 21
      tests/cases/garage/00-cli.sh
  79. 16
      tests/cases/garage/01-mc.sh
  80. 114
      tests/entrypoint.sh
  81. 3
      tests/utils/register-cleanup.sh
  82. 16
      tests/utils/shared-daemon-env.sh
  83. 93
      tests/utils/with-1-data-1-empty-node-cluster.sh
  84. 9
      tests/utils/with-tmp-for-case.sh

@ -1,4 +0,0 @@
#!/bin/sh
export PATH=$APPDIR/bin
exec entrypoint "$@"

@ -1,6 +1,6 @@
[Desktop Entry]
Name=Cryptic Net
Name[en]=Cryptic Net
Name=Isle
Name[en]=Isle
Exec=AppRun
Icon=cryptic-logo

@ -1,12 +1,12 @@
#
# This file defines all configuration directives which can be modified for
# the cryptic-net daemon at runtime. All values specified here are the
# the isle daemon at runtime. All values specified here are the
# default values.
#
################################################################################
# A DNS service runs as part of every cryptic-net process.
# A DNS service runs as part of every isle process.
dns:
# list of IPs that the DNS service will use to resolve requests outside the
@ -15,7 +15,7 @@ dns:
- 1.1.1.1
- 8.8.8.8
# A VPN service runs as part of every cryptic-net process.
# A VPN service runs as part of every isle process.
vpn:
# Enable this field if the vpn will be made to be publicly accessible at a
@ -52,6 +52,10 @@ vpn:
# That's it.
tun:
# Name of the tun network device which will route VPN traffic.
device: isle-tun
storage:
# Allocations defined here are used to store data in the distributed storage

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

@ -4,9 +4,9 @@ rely on it for anything._**
-----
# cryptic-net
# Isle
The cryptic-net project provides the foundation for an **autonomous community
The Isle project provides the foundation for an **autonomous community
cloud infrastructure**.
This project targets communities of individuals, where certain members of the
@ -14,7 +14,7 @@ community would like to host services and applications from servers running in
their homes or offices. These servers can range from simple Raspberry Pis to
full-sized home PCs.
The core components of cryptic-net, currently, are:
The core components of Isle, currently, are:
* A VPN which enables direct peer-to-peer communication. Even if most hosts in
the network are on a private LAN (e.g. their home WiFi network) or have a
@ -28,7 +28,7 @@ The core components of cryptic-net, currently, are:
within the network.
These components are wrapped into a single binary, with all setup being
automated. cryptic-net takes "just works" very seriously.
automated. Isle takes "just works" very seriously.
Participants are able to build upon these foundations to host services for
themselves and others. They can be assured that their communications are private
@ -37,9 +37,9 @@ third parties involved.
## Documentation
cryptic-net users fall into different roles, depending on their level of
involvement and expertise within their particular network. The documentation for
cryptic-net is broken down by these categories, so that the reader can easily
Isle users fall into different roles, depending on their level of
involvement and expertise within their particular network. The documentation
is broken down by these categories, so that the reader can easily
decide which documents they need to care about.
### User Docs
@ -90,16 +90,17 @@ Documentation for admins:
### Dev Docs
Devs may or may not be users in any particular cryptic network. They instead are
those who work on the actual code for cryptic-net.
Devs may or may not be users in any particular isle network. They instead are
those who work on the actual code for Isle.
Documentation for devs:
* [Design Principles](docs/dev/design-principles.md)
* [`cryptic-net daemon` process tree](docs/dev/daemon-process-tree.svg): Diagram
describing the [pmux](https://code.betamike.com/cryptic-io/pmux) process tree
created by `cryptic-net daemon` at runtime.
* [`isle daemon` process tree](docs/dev/daemon-process-tree.svg): Diagram
describing the [pmux](https://code.betamike.com/micropelago/pmux) process tree
created by `isle daemon` at runtime.
* [Rebuilding Documentation](docs/dev/rebuilding-documentation.md)
* [Releases](docs/dev/releases.md)
## Misc

@ -1,113 +1,178 @@
{
pkgsAttrs ? (import ./nix/pkgs.nix),
buildSystem ? builtins.currentSystem,
hostSystem ? buildSystem,
pkgsNix ? (import ./nix/pkgs.nix),
revision ? "dev",
releaseName ? "dev",
bootstrap ? null,
releaseName ? "debug",
}: let
pkgs = pkgsAttrs.pkgs;
in rec {
pkgs = pkgsNix.default {
inherit buildSystem hostSystem;
};
rootedBootstrap = pkgs.stdenv.mkDerivation {
name = "cryptic-net-rooted-bootstrap";
pkgsNative = pkgsNix.default {
inherit buildSystem;
hostSystem = buildSystem;
};
src = bootstrap;
garageNix = (import ./nix/garage.nix);
builder = builtins.toFile "builder.sh" ''
source $stdenv/setup
mkdir -p "$out"/share
cp "$src" "$out"/share/bootstrap.yml
'';
};
in rec {
version = pkgs.stdenv.mkDerivation {
name = "cryptic-net-version";
name = "isle-version";
inherit buildSystem hostSystem revision releaseName;
buildInputs = [ pkgs.git pkgs.go ];
nativeBuildInputs = [ pkgsNative.git ];
src = ./.;
inherit releaseName;
nixPkgsVersion = pkgsAttrs.version;
nixPkgsRev = pkgsAttrs.rev;
builtByUser = builtins.getEnv "USER";
goVersion = pkgs.go.version;
garageVersion = garageNix.version;
nixpkgsVersion = pkgsNix.version;
builder = builtins.toFile "builder.sh" ''
source $stdenv/setup
versionFile=version
cp -r "$src" srcCp
echo "Release: $releaseName" >> "$versionFile"
echo "Git Revision: $(cd srcCp && git rev-parse HEAD)" >> "$versionFile"
echo "Build date: $(date) ($(date +%s))" >> "$versionFile"
echo "Built by: $builtByUser" >> "$versionFile"
echo "Go version: $(go version)" >> "$versionFile"
echo "Nixpkgs version: $nixPkgsVersion ($nixPkgsRev)" >> "$versionFile"
echo "Release: $releaseName" >> "$versionFile"
echo "Platform: $hostSystem" >> "$versionFile"
echo "Git Revision: $revision" >> "$versionFile"
echo "Go Version: $goVersion" >> "$versionFile"
echo "Garage Version: $garageVersion" >> "$versionFile"
echo "NixPkgs Version: $nixpkgsVersion" >> "$versionFile"
echo "Build Platform: $buildSystem" >> "$versionFile"
mkdir -p "$out"/share
cp "$versionFile" "$out"/share
'';
};
entrypoint = pkgs.callPackage ./entrypoint {};
goBinaries = pkgs.buildGoModule {
pname = "isle-go-binaries";
version = "unstable";
# If this seems pointless, that's because it is! buildGoModule doesn't like
# it if the src derivation's name ends in "-go". So this mkDerivation here
# only serves to give buildGoModule a src derivation with a name it likes.
src = pkgs.stdenv.mkDerivation {
name = "isle-go-src";
src = ./go;
builder = builtins.toFile "builder.sh" ''
source $stdenv/setup
cp -r "$src" "$out"
'';
};
vendorSha256 = "sha256-P1TXG0fG8/6n37LmM5ApYctqoZzJFlvFAO2Zl85SVvk=";
subPackages = [
"./cmd/entrypoint"
];
};
dnsmasq = (pkgs.callPackage ./nix/dnsmasq.nix {
glibcStatic = pkgs.glibc.static;
stdenv = pkgs.pkgsStatic.stdenv;
});
nebula = pkgs.callPackage ./nix/nebula.nix {};
garage = (pkgs.callPackage ./nix/garage.nix {}).env;
garage = let
waitFor = pkgs.callPackage ./nix/wait-for.nix {};
hostPlatform = pkgs.stdenv.hostPlatform.parsed;
appDir = pkgs.buildEnv {
name = "cryptic-net-AppDir";
paths = [
in pkgs.callPackage garageNix.package {
./AppDir
version
dnsmasq
nebula
garage
entrypoint
inherit buildSystem;
hostSystem = "${hostPlatform.cpu.name}-unknown-${hostPlatform.kernel.name}-musl";
pkgsSrc = pkgsNix.src;
] ++ (if bootstrap != null then [ rootedBootstrap ] else []);
};
appimagetool = pkgs.callPackage ./nix/appimagetool.nix {};
rootedBootstrap = pkgs.stdenv.mkDerivation {
name = "isle-rooted-bootstrap";
appImage = pkgs.stdenv.mkDerivation {
name = "cryptic-net-AppImage";
src = appDir;
src = bootstrap;
builder = builtins.toFile "builder.sh" ''
source $stdenv/setup
mkdir -p "$out"/share
cp "$src" "$out"/share/bootstrap.yml
'';
};
appDir = pkgs.stdenv.mkDerivation {
name = "isle-AppDir";
src = pkgs.buildEnv {
name = "isle-AppDir-base";
paths = [
buildInputs = [ appimagetool ];
./AppDir
version
dnsmasq
nebula
garage
pkgs.minio-client
goBinaries
ARCH = "x86_64";
] ++ (if bootstrap != null then [ rootedBootstrap ] else []);
};
builder = builtins.toFile "build.sh" ''
source $stdenv/setup
cp -rL "$src" cryptic-net
chmod +w cryptic-net -R
appimagetool cryptic-net "$out"
cp -rL "$src" "$out"
chmod +w "$out" -R
cd "$out"
cp ./bin/entrypoint ./AppRun
'';
};
release = pkgs.stdenv.mkDerivation {
name = "cryptic-net-AppImage";
inherit appImage releaseName;
appimagetool = pkgs.callPackage ./nix/appimagetool.nix {};
appImage = pkgs.stdenv.mkDerivation {
name = "isle-AppImage";
src = appDir;
nativeBuildInputs = [
appimagetool
];
buildInputs = [ pkgs.coreutils ];
ARCH = pkgs.stdenv.hostPlatform.parsed.cpu.name;
builder = builtins.toFile "build.sh" ''
source $stdenv/setup
cp -rL "$src" isle.AppDir
chmod +w isle.AppDir -R
export VERSION=debug
# https://github.com/probonopd/go-appimage/issues/155
unset SOURCE_DATE_EPOCH
mkdir -p "$out"
cp "$appImage" "$out"/cryptic-net-$releaseName-linux-amd64
(cd "$out" && sha256sum * > sha256.txt)
appimagetool ./isle.AppDir
mkdir -p "$out"/bin
chmod +w "$out" -R
mv Isle-* "$out"/bin/isle
'';
};
tests = pkgs.writeScript "isle-tests" ''
export PATH=${pkgs.lib.makeBinPath [
appImage
pkgs.busybox
pkgs.yq-go
pkgs.jq
pkgs.dig
]}
export SHELL=${pkgs.bash}/bin/bash
exec ${pkgs.bash}/bin/bash ${./tests}/entrypoint.sh "$@"
'';
}

@ -4,16 +4,16 @@ This document guides an admin through adding a single host to the network. Keep
in mind that the steps described here must be done for _each_ host the user
wishes to add.
There are two ways for a user to add a host to the cryptic network.
There are two ways for a user to add a host to the isle network.
- If the user is savy enough to obtain their own `cryptic-net` binary, they can
- If the user is savy enough to obtain their own `isle` binary, they can
do so. The admin can then generate a `bootstrap.yml` file for their host,
give that to the user, and the user can run `cryptic-net daemon` using that
give that to the user, and the user can run `isle daemon` using that
bootstrap file.
- If the user is not so savy, the admin can generate a custom `cryptic-net`
- If the user is not so savy, the admin can generate a custom `isle`
binary with the `bootstrap.yml` embedded into it. The user can be given this
binary and run `cryptic-net daemon` without any configuration on their end.
binary and run `isle daemon` without any configuration on their end.
From the admin's perspective the only difference between these cases is one
extra step.
@ -43,7 +43,7 @@ To create a `bootstrap.yml` file for the new host, the admin should perform the
following command from their own host:
```
cryptic-net hosts create-bootstrap \
isle admin create-bootstrap \
--hostname <name> \
--ip <ip> \
--admin-path <path to admin.yml> \
@ -54,9 +54,9 @@ The resulting `bootstrap.yml` file should be treated as a secret file that is
shared only with the user it was generated for. The `bootstrap.yml` file should
not be re-used between hosts either.
If the user already has access to a `cryptic-net` binary then the new
If the user already has access to a `isle` binary then the new
`bootstrap.yml` file can be given to them as-is, and they can proceed with
running their host's `cryptic-net daemon`.
running their host's `isle daemon`.
### Encrypted `admin.yml`
@ -66,7 +66,7 @@ GPG is being used to secure `admin.yml` then the following could be used to
generate a `bootstrap.yml`:
```
gpg -d <path to admin.yml.gpg> | cryptic-net hosts create-bootstrap \
gpg -d <path to admin.yml.gpg> | isle admin create-bootstrap \
--hostname <name> \
--ip <ip> \
--admin-path - \

@ -1,9 +1,9 @@
# Creating a New Network
This guide is for those who wish to start a new cryptic network of their
This guide is for those who wish to start a new isle network of their
own.
By starting a new cryptic network, you are becoming the administrator of a
By starting a new isle network, you are becoming the administrator of a
network. Be aware that being a network administrator is not necessarily easy,
and the users of your network will frequently need your help in order to have a
good experience. It can be helpful to have others with which you are
@ -42,7 +42,7 @@ A `daemon.yml` will need to be created for use during network creation. You can
create a new `daemon.yml` with default values filled in by doing:
```
cryptic-net admin create-network --dump-config > /path/to/daemon.yml
isle admin create-network --dump-config > /path/to/daemon.yml
```
Open this file in a text editor and perform the following changes:
@ -70,7 +70,7 @@ be chosen with care.
networks](https://en.wikipedia.org/wiki/IPv4#Private_networks), but within
that selection the choice is up to you.
* Domain: cryptic-net is shipped with a DNS server which will automatically
* Domain: isle is shipped with a DNS server which will automatically
configure itself with all hosts in the network, with each DNS entry taking the
form of `hostname.hosts.domain`, where `domain` is the domain chosen in this
step. The domain may be a valid public domain or not, it's up to you.
@ -86,7 +86,7 @@ be chosen with care.
## Step 3: Prepare to Encrypt `admin.yml`
The `admin.yml` file (which will be created in the next step) is the most
sensitive part of a cryptic network. If it falls into the wrong hands it can be
sensitive part of an isle network. If it falls into the wrong hands it can be
used to completely compromise your network, impersonate hosts on the network,
and will likely lead to someone stealing or deleting all of your data.
@ -103,7 +103,7 @@ To create the `admin.yml` file, which effectively creates the network itself,
you can run:
```
sudo cryptic-net admin create-network \
sudo isle admin create-network \
--config-path /path/to/daemon.yml \
--name <name> \
--ip-net <ip/subnet-prefix> \
@ -132,10 +132,10 @@ At this point you should have an `admin.yml.gpg` file in your current directory.
## Step 5: Run the Daemon
The cryptic-net daemon can be run now, using the following command:
The isle daemon can be run now, using the following command:
```
sudo cryptic-net daemon -c /path/to/daemon.yml
sudo isle daemon -c /path/to/daemon.yml
```
**NOTE** that you _must_ use the same `daemon.yml` file used when creating the
@ -145,9 +145,4 @@ At this point your host, and your network, are ready to go! You can reference
the [Getting Started](../user/getting-started.md) document to set up your
host's daemon process in a more permanent way.
## Next Steps
* Add users
* Fix directories
[ddns]: https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/

@ -1,7 +1,7 @@
@startuml
hide empty description
state "./cryptic-net daemon -c ./daemon.yml" as init
state "./isle daemon -c ./daemon.yml" as init
state AppDir {
@ -39,4 +39,3 @@ state AppDir {
}
@enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

@ -1,21 +1,20 @@
# Design Principles
The following points form the basis for all design decisions made within the
cryptic-net project.
Isle project.
* The UX is aggressively optimized to eliminate manual intervention by users.
All other concerns are secondary. The concept of "UX" extends beyond GUI
interfaces, and encompasses all interactions of any sort with a cryptic-net
interfaces, and encompasses all interactions of any sort with an isle
process.
* All resources within a cryptic-net are expected to be hosted on hardware owned
by community members, for example home media servers or gaming rigs. Thus, a
cryptic-net is fully autonomous.
* All resources within an isle network are expected to be hosted on hardware
owned by community members, for example home media servers or gaming rigs.
Thus, an isle is fully autonomous.
* Hardware resources are expected to be heterogenous and geographically
dispersed.
* It is expected that a single host might be a part of multiple, independent
cryptic networks. These should not conflict with each other, nor share
resources.
isle networks. These should not conflict with each other, nor share resources.

@ -1,6 +1,6 @@
# Rebuilding Documentation
Most documentation for cryptic-net takes the form of markdown (`.md`) files,
Most documentation for Isle takes the form of markdown (`.md`) files,
which do not require any build step. There are a few other kinds of files, such
as `.plantuml` files, which do require a build step. If these are changed then
their artifacts should be rebuilt by doing:

@ -0,0 +1,32 @@
# Releases
A release consists of:
- A full set of binaries for all supported platforms, compiled from the same
source.
- A text file containing hashes of each binary.
- A file containing a signature of the hash file, created by whoever is building
the release.
## Building
*NOTE: This has only been tested from an x86_64 linux machine*
To create a release only a functional nix installation is required. Simply run
the `./release.sh` script, and input a release name when prompted.
From here an `isle` binary will be cross-compiled for all supported
platforms. This will take a long time the first time you perform it on your
machine.
Once compilation is completely, the release will be signed using the default GPG
key on your machine, and you will be prompted for its password in order to
create the signature.
## Releasing
Releases are uploaded to the repository's Releases page, and release naming
follows the conventional semantic versioning system. Each release should be
accompanied by a set of changes which have occurred since the last release,
described both in the `CHANGELOG.md` file and in the description on the Release
itself.

@ -4,14 +4,17 @@ The purpose of this document is define the specific terms which should be used
for various concepts, with the goal of establishing consistency throughout
documentation and source code.
- "user" - a person who takes part in the usage, operation, or administration of
a cryptic network.
- "Isle" - The name of this project, which is a proper noun and so should always
be capitalized.
- "host" - A computer or device used by a user to connect to a cryptic network.
- "isle" - The name of the binary or program produced by the Isle project. isle
is the name of the file itself, and so is always lower-case.
- "cryptic network", "network" - A collection of hosts which communicate and
share resources with each other via the mechanisms provided by the cryptic-net
project.
- "host" - A computer or device on which isle is run.
- "isle network", "network" - A collection of hosts which communicate and share
resources with each other via the Isle project.
- "user" - A person who takes part in the usage, operation, or administration of
an isle network.
- "cryptic-net" - The name of the binary or program which is used to interact
with a cryptic network.

@ -1,7 +1,7 @@
# Contributing a Lighthouse
The [nebula][nebula] project provides the VPN component which is used by
cryptic-net. Every nebula network requires at least one (but preferably more)
Isle. Every nebula network requires at least one (but preferably more)
publicly accessible hosts. These hosts are called lighthouses.
Lighthouses do _not_ route traffic between hosts on the VPN. Rather, they
@ -10,7 +10,7 @@ NAT punching through any NATs that hosts might be behind. As such, they are very
lightweight to run, and require no storage resources at all.
If your host machine has a public static IP, or a dynamic public IP with
[dDNS][ddns] set up, then it can contribute a lighthouse for cryptic-net.
[dDNS][ddns] set up, then it can contribute a lighthouse.
[nebula]: https://github.com/slackhq/nebula
[ddns]: https://www.cloudflare.com/learning/dns/glossary/dynamic-dns/
@ -30,7 +30,7 @@ Configure your host's firewall to allow all UDP traffic on that port.
First, if you haven't already, [create a `daemon.yml`
file](../user/creating-a-daemonyml-file.md). This will be used to
configure your `cryptic-net daemon` process with the public address that other
configure your `isle daemon` process with the public address that other
hosts can find your daemon on.
## Edit daemon.yml
@ -41,6 +41,6 @@ port.
## Restart the Daemon
With the `daemon.yml` configured, you should restart your `cryptic-net daemon`
With the `daemon.yml` configured, you should restart your `isle daemon`
process. On startup the daemon will add its public address to the global
configuration, which other hosts will pick up on and begin using.

@ -8,7 +8,7 @@ to the network, then this document is for you.
First, if you haven't already, [create a `daemon.yml`
file](../user/creating-a-daemonyml-file.md). This will be used to
configure your `cryptic-net daemon` process with the storage locations and
configure your `isle daemon` process with the storage locations and
capacities you want to contribute.
## Edit `daemon.yml`
@ -31,13 +31,13 @@ storage:
allocations:
# 1.2 TB are being shared from drive1
- data_path: /mnt/drive1/cryptic-net/data
meta_path: /mnt/drive1/cryptic-net/meta
- data_path: /mnt/drive1/isle/data
meta_path: /mnt/drive1/isle/meta
capacity: 1200
# 100 GB (the minimum) are being shared from drive2
- data_path: /mnt/drive2/cryptic-net/data
meta_path: /mnt/drive2/cryptic-net/meta
- data_path: /mnt/drive2/isle/data
meta_path: /mnt/drive2/isle/meta
capacity: 100
```
@ -48,17 +48,17 @@ is properly set up for providing storage.
## Restart the Daemon
With the `daemon.yml` configured, you should restart your `cryptic-net daemon`
With the `daemon.yml` configured, you should restart your `isle daemon`
process.
The `cryptic-net daemon` will automatically allow the ports used for your
The `isle daemon` will automatically allow the ports used for your
storage allocations in the vpn firewall.
## Further Reading
cryptic-net uses the [garage][garage] project for its storage system. See the
Isle uses the [garage][garage] project for its storage system. See the
[Managing Garage](managing-garage.md) document for more
information on how to interact directly with the garage instance being run by
cryptic-net.
isle.
[garage]: https://garagehq.deuxfleurs.fr/documentation/quick-start/

@ -10,29 +10,28 @@ the host's firewall, and the VPN firewall.
## VPN Firewall
cryptic-net uses the [nebula](https://github.com/slackhq/nebula) project to
Isle uses the [nebula](https://github.com/slackhq/nebula) project to
provide its VPN layer. Nebula ships with its own [builtin
firewall](https://nebula.defined.net/docs/config/firewall), which only applies
to connections coming in over the virtual network interface which it creates.
This firewall can be manually configured as part of cryptic-net's
This firewall can be manually configured as part of isle's
[`daemon.yml`](../user/creating-a-daemonyml-file.md) file.
Any storage instances which are defined as part of the `daemon.yml` file will
have their network ports automatically added to the VPN firewall by cryptic-net.
have their network ports automatically added to the VPN firewall by isle.
This means that you only need to configure the VPN firewall if you are hosting
services for your cryptic network besides storage.
services for your isle network besides storage.
## Host Firewall
The host you are running cryptic-net on will almost definitely have a firewall
The host you are running isle on will almost definitely have a firewall
running, separate from the VPN firewall. If you wish to provide services for
your cryptic network from your host, you will need to allow their ports in your
your isle network from your host, you will need to allow their ports in your
host's firewall.
**cryptic-net does _not_ automatically configure your host's firewall to any
extent!**
**isle does _not_ automatically configure your host's firewall to any extent!**
One option is to open your host to all traffic from your cryptic network, and
One option is to open your host to all traffic from your isle network, and
allow the VPN firewall to be fully responsible for filtering traffic. To do this
on Linux using iptables, for example, you would add something like this to your
iptables configuration:

@ -1,11 +1,11 @@
# Managing Garage
The garage project provides the network storage component for
cryptic-net. If you're reading this document then you would likely benefit
Isle. If you're reading this document then you would likely benefit
greatly from reading the [garage documentation][garage] on their website. It's
extremely well written and concise.
Note that the `cryptic-net daemon` process will handle all setup steps described
Note that the `isle daemon` process will handle all setup steps described
in that documentation, but it's still good to have an understanding of how
garage works and what it can do.
@ -13,12 +13,12 @@ garage works and what it can do.
## Garage Runtime Note
There is an important thing to note regarding how cryptic-net runs garage. As
There is an important thing to note regarding how isle runs garage. As
described in the [Contributing Storage](contributing-storage.md) document, a
single `cryptic-net daemon` process can be configured to provide any number of
single `isle daemon` process can be configured to provide any number of
storage allocations.
For each allocation which is configured, `cryptic-net daemon` will configure and
For each allocation which is configured, `isle daemon` will configure and
run a separate `garage server` instance as a sub-process. Each garage will use
the host's name as its zone in the garage cluster layout, which means that the
cluster will prefer to not replicate the same data within the same host, but may
@ -26,14 +26,14 @@ do so if necessary.
## Garage CLI
Every `cryptic-net` binary contains a full `garage` binary embedded into it.
Every `isle` binary contains a full `garage` binary embedded into it.
This binary can be accessed directly like so:
```
sudo cryptic-net garage cli <subcmd> <args>
sudo isle garage cli <subcmd> <args>
```
Before handing off execution to the `garage` binary, the `cryptic-net` process
Before handing off execution to the `garage` binary, the `isle` process
will automatically set up the RPC host and secret environment variables.
If the host which is running the command has more than one allocation
@ -47,7 +47,7 @@ connected to.
To display the current layout of the garage cluster:
```
sudo cryptic-net garage cli layout show
sudo isle garage cli layout show
```
**(DO NOT CHANGE THE CLUSTER LAYOUT UNLESS YOU KNOW WHAT YOU'RE DOING!)**
@ -55,11 +55,11 @@ sudo cryptic-net garage cli layout show
To create a new bucket:
```
sudo cryptic-net garage cli bucket create new-bucket
sudo isle garage cli bucket create new-bucket
```
To list existing buckets:
```
sudo cryptic-net garage cli bucket list
sudo isle garage cli bucket list
```

@ -8,15 +8,59 @@ order they will be implemented.
These items are listed more or less in the order they need to be completed, as
they generally depend on the items previous to them.
### Cross Compilation
### Window Support + GUI
Currently the only supported OS/CPU is Linux/amd64. This can be expanded
_theoretically_ quite easily, using nix's cross compilation tools. First target
should be OSX/arm64, but windows would also be quite the get.
Support for Windows is a must. This requirement also includes a simple GUI,
which would essentially act as a thin layer on top of `daemon.yml` to start
with.
### Testing
Depending on difficulty level, OSX support might be added at this stage as well.
Once bootstrap is generalized, we'll be able to write some automated tests.
### NATS
Garage is currently used to handle eventually-consistent persistent storage, but
there is no mechanism for inter-host realtime communication as of yet. NATS
would be a good candidate for this, as it uses a gossip protocol which does not
require a central coordinator (I don't think), and is well supported.
### Integration of [domani](https://code.betamike.com/micropelago/domani)
Integration of domani will require some changes on domani's end. We want domani
to be able to store cert information in S3 (garage), so that all isle lighthouse
nodes can potentially become gateways as well. Once done, it would be possible
for lighthouses to forward public traffic to inner nodes.
It should also be possible for users within the network to take advantage of
domani's hosting ability even without an always-on host of their own, without
requiring a passphrase.
Most likely this integration will require NATS as well, to coordinate cache
invalidation and cert refreshing.
### Invitation code bootstrapping
Once an HTTP gateway/load-balancer is set up it should be possible to do host
bootstrapping using invite codes rather than manually giving new users bootstrap
files. The bootstrap file would be stored, encrypted, in garage, with the invite
code being able to both identify and decrypt it. To instantiate a host, the user
only needs to input the network domain name and the invite code.
### FUSE Mount
KBFS style. Every user should be able to mount virtual directories to their host
which correspond to various buckets in garage.
- "public": editable amongst all users on the host, shared publicly via HTTP
gateway.
- "protected": editable amongst all users on the host, but not accessible
outside the network.
- "private": only accessible to a particular user (client-side encrypted).
Whether it's necessary to support directories which are shared only between
specific users remains to be seen. The identification of a single "user" between
different hosts is also an unsolved problem.
## Side quests
@ -24,66 +68,66 @@ These items aren't necessarily required by the main quest, and aren't dependent
on any other items being completed. They are nice-to-haves that we do want to
eventually complete, but aren't the main focus.
### DNS resolver settings
### Design System
The daemon should update the resolver settings of the host, so that it
automatically serves DNS queries, unless set to not do so in `daemon.yml`.
It would be great to get some help from a designer or otherwise
artistically-minded person to create some kind of design framework which could
be used across publicly-facing frontends. Such a system would provide a simple
but cohesive vision for how things should look, include:
### Install sub-command
- Color schemes
- Fonts and text decoration in different situations
- Some simple, reusable layout templates (splash page, documentation, form)
- Basic components like tables, lists, media, etc..
It would be great to have a `cryptic-net install` sub-command which would
auto-detect the installed operating system and install the daemon automatically.
### DHCP
### Web server + interface
Currently all hosts require a static IP to be reserved by the admin. Nebula may
support DHCP already, but if it doesn't we should look into how this could be
accomplished. Depending on how reliable DNS support is it may be possible to use
DHCP for all non-lighthouse hosts, which would be excellent.
One idea is to have every `cryptic-net daemon` run a webserver as one of its
sub-processes. This server could serve multiple functions:
### IPv6 network ranges
- Possible public gateway, if the host is configured to be public, into internal
cryptic-net services. This will require some sort of service discovery
mechanism that other hosts in the network can easily hook into via their
`daemon.yml`s. Ideally this mechanism would involve little beyond updating
entries in garage, which the public gateways then pick up on.
It should theoretically be possible for the internal network IP range to be on
IPv6 rather than IPv4. This may be a simple matter of just testing it to confirm
it works.
One wrinkle here will be TLS certificates. Ideally internal hosts could host
websites publicly via the gateways on their network, using arbitrary domains
that the users own. The gateways will need some way of obtaining certs for
these domains automatically (or as automatically as possible).
### Proper Linux Packages
- Local interface for the `cryptic-net daemon` process. For example, status and
connectivity information for the local host could be provided via a simple web
interface, which the user can open in their browser. This saves us the effort
of needing to develop UIs for individual OSs. This could also make remotely
debugging hosts easier for admins.
Rather than distributing raw binaries for Linux we should instead be
distributing actual packages, e.g. deb files for debian/ubuntu, PKGBUILD for
arch, rpm for fedora (if we care), etc... This will allow for properly setting
capabilities for the binary at install time, so that it can be run as non-root,
and installing any necessary `.desktop` files so that it can be run as a GUI
application.
### Mobile app
To start with a simple mobile app which provided connectivity to the network
would be great. Such a mobile app could be based on the existing
[mobile_nebula](https://github.com/DefinedNet/mobile_nebula). The main changes
needed would be:
- Allow importing a `bootstrap.yml` file, rather than requiring manual setup by
users.
- Set device's DNS settings. There is an [open
PR](https://github.com/DefinedNet/mobile_nebula/pull/18) for android to do
this upstream.
- Rebranding and possibly submitting to Apple app store (bleh).
would be great. We are not able to use the existing nebula mobile app because it
is not actually open-source, but we can at least use it as a reference to see
how this can be accomplished.
### Don't run as root
It's currently a pretty hard requirement for `cryptic-net daemon` to run as
It's currently a pretty hard requirement for `isle daemon` to run as
root. This is due to:
- nebula's network interface root to be started.
- dnsmasq listening on port 53, generally a protected port.
If we can't figure out how to get these things running from the start as
non-privileged users, we at least need to get cryptic-net to drop priveleges
from root after initial startup.
On linux it should be fairly straightforward to grant the entrypoint the
necessary ambient capabilities up-front, and then drop down to a specified user.
This is how the tests work. Doing this with other OS's will depend on how they
work.
### DNS/Firewall Configuration
Ideally Isle could detect the DNS/firewall subsystems being used on a per-OS
basis and configure them as needed. This would be simplify necessary
documentation and setup steps for operators.
### Plugins

@ -1,14 +1,20 @@
{
pkgs ? (import ../nix/pkgs.nix).pkgs,
buildSystem ? builtins.currentSystem,
hostSystem ? buildSystem,
pkgsNix ? (import ../nix/pkgs.nix),
}: pkgs.mkShell {
name = "cryptic-net-build-docs";
buildInputs = [ pkgs.plantuml ];
shellHook = ''
set -e
plantuml -tsvg ./dev/*.plantuml
exit 0
'';
}
}: let
pkgs = pkgsNix.default {
inherit buildSystem hostSystem;
};
in
pkgs.mkShell {
name = "isle-build-docs";
buildInputs = [ pkgs.plantuml ];
shellHook = ''
set -e
plantuml -tsvg ./dev/*.plantuml
exit 0
'';
}

@ -1,6 +1,6 @@
# Creating a daemon.yml File
The `cryptic-net daemon` process has generally sane defaults and does not need
The `isle daemon` process has generally sane defaults and does not need
to be configured for most users. This document describes how to use the
`daemon.yml` file to handle those cases where configuration is necessary.
@ -10,11 +10,11 @@ First, create a `daemon.yml` file. You can create a new `daemon.yml` with
default values filled in by doing:
```
cryptic-net daemon --dump-config > /path/to/daemon.yml
isle daemon --dump-config > /path/to/daemon.yml
```
If you open that file in a text editor you can view all default values that
`cryptic-net daemon` ships with, as well as documentation for all configurable
`isle daemon` ships with, as well as documentation for all configurable
parameters. Feel free to edit this file as needed.
## Using daemon.yml
@ -23,10 +23,10 @@ With the `daemon.yml` created and configured, you can configure your daemon
process to use it by passing it as the `--config-path` argument:
```
sudo cryptic-net daemon --config-path /path/to/daemon.yml
sudo isle daemon --config-path /path/to/daemon.yml
```
If you are an operator then your host should be running its `cryptic-net daemon`
If you are an operator then your host should be running its `isle daemon`
process in systemd (see [Getting Started](getting-started.md) if
not), and you will need to modify the service file accordingly.

@ -1,25 +1,27 @@
# Getting Started
This document will guide you through the process of obtaining a cryptic-net
binary and joining the network.
This document will guide you through the process of obtaining an isle
binary and joining a network.
NOTE currently only linux machines with amd64/x86_64 processors are supported.
More OSs and architectures coming soon!
NOTE currently only linux machines with the following architectures are
supported:
- `x86_64` / `amd64`
- `aarch64` / `arm64`
- `i686`
## Obtaining a cryptic-net Binary
(Only `x86_64` has been tested.)
More OSs and architectures coming soon!
Every host can have a binary built for it which has all configuration for that
host embedded directly into it. Such binaries require no extra configuration by
the user to use, and have no dependencies on anything else in the user's system.
## Obtaining an isle Binary
The process of obtaining a custom binary for your host is quite simple: ask an
admin of your network to give you one!
### The Easy Way
Note that if you'd like to join the network on multiple devices, each device
will needs its own binary, so be sure to tell your admin how many you want to
add and their names.
Download the latest binary for your platform from
[this link](https://code.betamike.com/micropelago/isle/releases/latest).
### Obtaining a cryptic-net Binary, the Hard Way
### The Hard Way
Alternatively, you can build your own binary by running the following from the
project's root:
@ -28,7 +30,7 @@ project's root:
nix-build -A appImage
```
(*NOTE* Dependencies of `cryptic-net` seemingly compile all of musl and rust
(*NOTE* Dependencies of `isle` seemingly compile all of musl and rust
from scratch (it's not clear why, blame garage!). If you have not otherwise
configured it, nix might be using a tmpfs as its build directory, and the
capacity of this tmpfs will probably be exceeded by this build. You can change
@ -37,24 +39,28 @@ variable for `nix-daemon` (see [this github issue][tmpdir-gh].))
The resulting binary can be found in the `result` directory which is created.
In this case you will need an admin to provide you with a `bootstrap.yml` for
your host, rather than a custom binary. When running the daemon in the following
steps you will need to provide the `--bootstrap-path` CLI argument to the daemon
process.
[tmpdir-gh]: https://github.com/NixOS/nix/issues/2098#issuecomment-383243838
## Obtaining Your Bootstrap File
The `bootstrap.yml` file contains all information required for your particular
host to join the network, and must be generated and provided to you by an admin
for the network.
## Running the Daemon
Once you have a binary, you will need to run the `daemon` sub-command as the
root user. This can most easily be done using the `sudo` command, in a terminal:
Once you have a binary and bootstrap file, you will need to run the `daemon`
sub-command as the root user. This can most easily be done using the `sudo`
command, in a terminal:
```
sudo /path/to/cryptic-net daemon
sudo /path/to/isle daemon --bootstrap-path /path/to/bootstrap.yml
```
This will start the daemon process, which will keep running until you kill it
with `ctrl-c`.
with `ctrl-c`. The `--bootstrap-path /path/to/bootstrap.yml` argument is only
required the first time the daemon is run, it will be ignored on subsequent
runs.
You can double check that the daemon is running properly by pinging a private IP
from the network in a separate terminal:
@ -74,12 +80,12 @@ Rather than running the daemon manually, you can install it as a systemd
service. This way your daemon will automatically start in the background on
startup, and will be restarted if it has any issues.
To do so, create a file at `/etc/systemd/system/cryptic-net.service` with the
To do so, create a file at `/etc/systemd/system/isle.service` with the
following contents:
```
[Unit]
Description=cryptic-net
Description=isle
Requires=network.target
After=network.target
@ -87,13 +93,13 @@ After=network.target
Restart=always
RestartSec=1s
User=root
ExecStart=/path/to/cryptic-net daemon
ExecStart=/path/to/isle daemon
[Install]
WantedBy=multi-user.target
```
Remember to change the `/path/to/cryptic-net` part to the actual absolute path
Remember to change the `/path/to/isle` part to the actual absolute path
to your binary!
Once created, perform the following commands in a terminal to enable the
@ -101,17 +107,17 @@ service:
```
sudo systemctl daemon-reload
sudo systemctl enable --now cryptic-net
sudo systemctl enable --now isle
```
You can check the service's status by doing:
```
sudo systemctl status cryptic-net
sudo systemctl status isle
```
and you can view its full logs by doing:
```
sudo journalctl -lu cryptic-net
sudo journalctl -lu isle
```

@ -1,6 +1,6 @@
# Using DNS
Every `cryptic-net daemon` process ships with a DNS server which runs
Every `isle daemon` process ships with a DNS server which runs
automatically. This server will listen on port 53 on the VPN IP of that
particular host.
@ -13,14 +13,14 @@ server will forward the request to a pre-configured public resolver. The set of
public resolvers used can be configured using the
[daemon.yml](creating-a-daemonyml-file.md) file.
This DNS server is an optional feature of cryptic-net, and not required in
general for making use of the network.
This DNS server is an optional feature of Isle, and not required in general for
making use of the network.
## Example
As an example of how to make use of this DNS server, let's say my host's IP on
the network is `10.10.1.1`, and my network's domain is `cool.internal`.
In order to configure the host to use the cryptic-net DNS server for all DNS
In order to configure the host to use the isle DNS server for all DNS
requests, I could do something like this:
```
@ -28,7 +28,7 @@ sudo su
echo "nameserver 10.10.1.1" > /etc/resolv.conf
```
From that point, all DNS requests on my host would hit the cryptic-net DNS
From that point, all DNS requests on my host would hit the isle DNS
server. If I request `my-host.hosts.cool.internal`, it would respond with the
appropriate private IP.

@ -1,14 +0,0 @@
{
buildGoModule,
}: buildGoModule {
pname = "cryptic-net-entrypoint";
version = "unstable";
src = ./src;
vendorSha256 = "sha256-TTTXwztv4xwF1uXcYoSka6HwgHwU1AnClF4fguXVtK4=";
subPackages = [
"cmd/entrypoint"
];
}

@ -1,105 +0,0 @@
// Package garage contains helper functions and types which are useful for
// setting up garage configs, processes, and deployments.
package garage
import (
"encoding/hex"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
)
const (
// Region is the region which garage is configured with.
Region = "garage"
// GlobalBucket is the name of the global garage bucket which is
// accessible to all hosts in the network.
GlobalBucket = "cryptic-net-global"
// ReplicationFactor indicates the replication factor set on the garage
// cluster. We currently only support a factor of 3.
ReplicationFactor = 3
)
func nodeKeyPath(metaDirPath string) string {
return filepath.Join(metaDirPath, "node_key")
}
func nodeKeyPubPath(metaDirPath string) string {
return filepath.Join(metaDirPath, "node_key.pub")
}
// LoadAllocID returns the peer ID (ie the public key) of the node at the given
// meta directory.
func LoadAllocID(metaDirPath string) (string, error) {
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
pubKey, err := os.ReadFile(nodeKeyPubPath)
if err != nil {
return "", fmt.Errorf("reading %q: %w", nodeKeyPubPath, err)
}
return hex.EncodeToString(pubKey), nil
}
// InitAlloc initializes the meta directory and keys for a particular
// allocation, if it hasn't been done so already. It returns the peer ID (ie the
// public key) in any case.
func InitAlloc(metaDirPath string) (string, error) {
var err error
exists := func(path string) bool {
if err != nil {
return false
} else if _, err = os.Stat(path); errors.Is(err, fs.ErrNotExist) {
err = nil
return false
} else if err != nil {
err = fmt.Errorf("checking if %q exists: %w", path, err)
return false
}
return true
}
nodeKeyPath := nodeKeyPath(metaDirPath)
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
nodeKeyPathExists := exists(nodeKeyPath)
nodeKeyPubPathExists := exists(nodeKeyPubPath)
if err != nil {
return "", err
} else if nodeKeyPubPathExists != nodeKeyPathExists {
return "", fmt.Errorf("%q or %q exist without the other existing", nodeKeyPath, nodeKeyPubPath)
} else if nodeKeyPathExists {
return LoadAllocID(metaDirPath)
}
// node key hasn't been written, write it
if err := os.MkdirAll(metaDirPath, 0750); err != nil {
return "", fmt.Errorf("making directory %q: %w", metaDirPath, err)
}
pubKey, privKey := GeneratePeerKey()
if err := os.WriteFile(nodeKeyPath, privKey, 0400); err != nil {
return "", fmt.Errorf("writing private key to %q: %w", nodeKeyPath, err)
} else if err := os.WriteFile(nodeKeyPubPath, pubKey, 0440); err != nil {
return "", fmt.Errorf("writing public key to %q: %w", nodeKeyPubPath, err)
}
return hex.EncodeToString(pubKey), nil
}

@ -0,0 +1,7 @@
{
"nodes": {
"root": {}
},
"root": "root",
"version": 7
}

@ -0,0 +1,36 @@
{
description = "isle provides the foundation for an autonomous community cloud infrastructure";
outputs = {
self,
}: let
supportedSystems = (import ./nix/pkgs.nix).supportedSystems;
mkPkg = (buildSystem: hostSystem: let
defaultAttrs = (import ./default.nix) {
inherit hostSystem buildSystem;
revision = if self ? rev then self.rev else "dirty";
releaseName = "flake";
};
in
defaultAttrs.appImage
);
pkgsForBuildSystem = (buildSystem: {
default = mkPkg buildSystem buildSystem;
});
in {
packages = (builtins.foldl'
(pkgs: buildSystem:
pkgs // { "${buildSystem}" = pkgsForBuildSystem buildSystem; })
{}
supportedSystems
);
};
}

@ -2,8 +2,8 @@
package admin
import (
"cryptic-net/garage"
"cryptic-net/nebula"
"isle/garage"
"isle/nebula"
"io"
"gopkg.in/yaml.v3"

@ -3,9 +3,9 @@
package bootstrap
import (
"cryptic-net/admin"
"cryptic-net/garage"
"cryptic-net/nebula"
"isle/admin"
"isle/garage"
"isle/nebula"
"crypto/sha512"
"fmt"
"io"

@ -1,7 +1,7 @@
package bootstrap
import (
"cryptic-net/garage"
"isle/garage"
)
// GaragePeers returns a Peer for each known garage instance in the network.

@ -3,8 +3,8 @@ package bootstrap
import (
"bytes"
"context"
"cryptic-net/garage"
"cryptic-net/nebula"
"isle/garage"
"isle/nebula"
"fmt"
"path/filepath"
"strings"

@ -2,7 +2,7 @@ package bootstrap
import (
"bytes"
"cryptic-net/nebula"
"isle/nebula"
"fmt"
"net"
"strings"

@ -2,20 +2,20 @@ package main
import (
"context"
"cryptic-net/admin"
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/garage"
"cryptic-net/nebula"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"isle/admin"
"isle/bootstrap"
"isle/daemon"
"isle/garage"
"isle/nebula"
"net"
"os"
"strings"
"code.betamike.com/cryptic-io/pmux/pmuxlib"
"code.betamike.com/micropelago/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
@ -50,7 +50,7 @@ func readAdmin(path string) (admin.Admin, error) {
var subCmdAdminCreateNetwork = subCmd{
name: "create-network",
descr: "Creates a new cryptic network, outputting the resulting admin.yml to stdout",
descr: "Creates a new isle network, outputting the resulting admin.yml to stdout",
do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
@ -183,7 +183,7 @@ var subCmdAdminCreateNetwork = subCmd{
hostBootstrap.Garage.AdminToken = randStr(32)
hostBootstrap.Garage.GlobalBucketS3APICredentials = garage.NewS3APICredentials()
if hostBootstrap, err = mergeDaemonConfigIntoBootstrap(hostBootstrap, daemonConfig); err != nil {
if hostBootstrap, daemonConfig, err = coalesceDaemonConfigAndBootstrap(hostBootstrap, daemonConfig); err != nil {
return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
}
@ -237,7 +237,7 @@ var subCmdAdminCreateNetwork = subCmd{
err = garageInitializeGlobalBucket(ctx, logger, hostBootstrap, daemonConfig)
if cErr := (garage.AdminClientError{}); errors.As(err, &cErr) && cErr.StatusCode == 409 {
return fmt.Errorf("shared global bucket has already been created, are the storage allocations from a previously initialized cryptic-net being used?")
return fmt.Errorf("shared global bucket has already been created, are the storage allocations from a previously initialized isle being used?")
} else if err != nil {
return fmt.Errorf("initializing garage shared global bucket: %w", err)
@ -344,6 +344,77 @@ var subCmdAdminCreateBootstrap = subCmd{
},
}
var subCmdAdminCreateNebulaCert = subCmd{
name: "create-nebula-cert",
descr: "Creates a signed nebula certificate file and writes it to stdout",
checkLock: false,
do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
hostName := flags.StringP(
"hostname", "h", "",
"Name of the host to generate bootstrap.yml for",
)
ipStr := flags.StringP(
"ip", "i", "",
"IP of the new host",
)
adminPath := flags.StringP(
"admin-path", "a", "",
`Path to admin.yml file. If the given path is "-" then stdin is used.`,
)
pubKeyPath := flags.StringP(
"public-key-path", "p", "",
`Path to PEM file containing public key which will be embedded in the cert.`,
)
if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
if *hostName == "" || *ipStr == "" || *adminPath == "" || *pubKeyPath == "" {
return errors.New("--hostname, --ip, --admin-path, and --pub-key-path are required")
}
if err := validateHostName(*hostName); err != nil {
return fmt.Errorf("invalid hostname %q: %w", *hostName, err)
}
ip := net.ParseIP(*ipStr)
if ip == nil {
return fmt.Errorf("invalid ip %q", *ipStr)
}
adm, err := readAdmin(*adminPath)
if err != nil {
return fmt.Errorf("reading admin.yml with --admin-path of %q: %w", *adminPath, err)
}
hostPubPEM, err := os.ReadFile(*pubKeyPath)
if err != nil {
return fmt.Errorf("reading public key from %q: %w", *pubKeyPath, err)
}
nebulaHostCertPEM, err := nebula.NewHostCertPEM(
adm.Nebula.CACredentials, string(hostPubPEM), *hostName, ip,
)
if err != nil {
return fmt.Errorf("creating cert: %w", err)
}
if _, err := os.Stdout.Write([]byte(nebulaHostCertPEM)); err != nil {
return fmt.Errorf("writing to stdout: %w", err)
}
return nil
},
}
var subCmdAdmin = subCmd{
name: "admin",
descr: "Sub-commands which only admins can run",
@ -351,6 +422,7 @@ var subCmdAdmin = subCmd{
return subCmdCtx.doSubCmd(
subCmdAdminCreateNetwork,
subCmdAdminCreateBootstrap,
subCmdAdminCreateNebulaCert,
)
},
}

@ -1,10 +1,10 @@
package main
import (
"cryptic-net/bootstrap"
"errors"
"fmt"
"io/fs"
"isle/bootstrap"
"os"
"path/filepath"
)
@ -15,7 +15,10 @@ func loadHostBootstrap() (bootstrap.Bootstrap, error) {
hostBootstrap, err := bootstrap.FromFile(dataDirPath)
if errors.Is(err, fs.ErrNotExist) {
return bootstrap.Bootstrap{}, errors.New("%q not found, has the daemon ever been run?")
return bootstrap.Bootstrap{}, fmt.Errorf(
"%q not found, has the daemon ever been run?",
dataDirPath,
)
} else if err != nil {
return bootstrap.Bootstrap{}, fmt.Errorf("loading %q: %w", dataDirPath, err)

@ -10,16 +10,16 @@ import (
"sync"
"time"
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"isle/bootstrap"
"isle/daemon"
"code.betamike.com/cryptic-io/pmux/pmuxlib"
"code.betamike.com/micropelago/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
// The daemon sub-command deals with starting an actual cryptic-net daemon
// process, which is required to be running for most other cryptic-net
// The daemon sub-command deals with starting an actual isle daemon
// process, which is required to be running for most other Isle
// functionality. The sub-command does the following:
//
// * Creates and locks the runtime directory.
@ -196,7 +196,7 @@ func runDaemonPmuxOnce(
var subCmdDaemon = subCmd{
name: "daemon",
descr: "Runs the cryptic-net daemon (Default if no sub-command given)",
descr: "Runs the isle daemon (Default if no sub-command given)",
do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
@ -213,7 +213,7 @@ var subCmdDaemon = subCmd{
bootstrapPath := flags.StringP(
"bootstrap-path", "b", "",
`Path to a bootstrap.yml file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the cryptic-net binary has a bootstrap built into it then this argument is always optional.`,
`Path to a bootstrap.yml file. This only needs to be provided the first time the daemon is started, after that it is ignored. If the isle binary has a bootstrap built into it then this argument is always optional.`,
)
logLevelStr := flags.StringP(
@ -305,7 +305,7 @@ var subCmdDaemon = subCmd{
// up-to-date possible bootstrap. This updated bootstrap will later get
// updated in garage using bootstrap.PutGarageBoostrapHost, so other
// hosts will see it as well.
if hostBootstrap, err = mergeDaemonConfigIntoBootstrap(hostBootstrap, daemonConfig); err != nil {
if hostBootstrap, daemonConfig, err = coalesceDaemonConfigAndBootstrap(hostBootstrap, daemonConfig); err != nil {
return fmt.Errorf("merging daemon config into bootstrap data: %w", err)
}

@ -2,18 +2,18 @@ package main
import (
"context"
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/garage"
"isle/bootstrap"
"isle/daemon"
"isle/garage"
"fmt"
"time"
)
func mergeDaemonConfigIntoBootstrap(
func coalesceDaemonConfigAndBootstrap(
hostBootstrap bootstrap.Bootstrap,
daemonConfig daemon.Config,
) (
bootstrap.Bootstrap, error,
bootstrap.Bootstrap, daemon.Config, error,
) {
host := bootstrap.Host{
@ -26,28 +26,30 @@ func mergeDaemonConfigIntoBootstrap(
if allocs := daemonConfig.Storage.Allocations; len(allocs) > 0 {
for _, alloc := range allocs {
for i, alloc := range allocs {
id, err := garage.InitAlloc(alloc.MetaPath)
id, rpcPort, err := garage.InitAlloc(alloc.MetaPath, alloc.RPCPort)
if err != nil {
return bootstrap.Bootstrap{}, fmt.Errorf("initializing alloc at %q: %w", alloc.MetaPath, err)
return bootstrap.Bootstrap{}, daemon.Config{}, fmt.Errorf("initializing alloc at %q: %w", alloc.MetaPath, err)
}
host.Garage.Instances = append(host.Garage.Instances, bootstrap.GarageHostInstance{
ID: id,
RPCPort: alloc.RPCPort,
RPCPort: rpcPort,
S3APIPort: alloc.S3APIPort,
})
allocs[i].RPCPort = rpcPort
}
}
hostBootstrap.Hosts[host.Name] = host
if err := writeBootstrapToDataDir(hostBootstrap); err != nil {
return bootstrap.Bootstrap{}, fmt.Errorf("writing bootstrap file: %w", err)
return bootstrap.Bootstrap{}, daemon.Config{}, fmt.Errorf("writing bootstrap file: %w", err)
}
return hostBootstrap, nil
return hostBootstrap, daemonConfig, nil
}
func doOnce(ctx context.Context, fn func(context.Context) error) error {

@ -1,14 +1,14 @@
package main
import (
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/dnsmasq"
"isle/bootstrap"
"isle/daemon"
"isle/dnsmasq"
"fmt"
"path/filepath"
"sort"
"code.betamike.com/cryptic-io/pmux/pmuxlib"
"code.betamike.com/micropelago/pmux/pmuxlib"
)
func dnsmasqPmuxProcConfig(
@ -45,7 +45,7 @@ func dnsmasqPmuxProcConfig(
return pmuxlib.ProcessConfig{
Name: "dnsmasq",
Cmd: "dnsmasq",
Cmd: binPath("dnsmasq"),
Args: []string{"-d", "-C", confPath},
}, nil
}

@ -7,9 +7,31 @@ import (
"syscall"
)
// minio-client keeps a configuration directory which contains various pieces of
// information which may or may not be useful. Unfortunately when it initializes
// this directory it likes to print some annoying logs, so we pre-initialize in
// order to prevent it from doing so.
func initMCConfigDir() (string, error) {
var (
path = filepath.Join(envDataDirPath, "mc")
sharePath = filepath.Join(path, "share")
configJSONPath = filepath.Join(path, "config.json")
)
if err := os.MkdirAll(sharePath, 0700); err != nil {
return "", fmt.Errorf("creating %q: %w", sharePath, err)
}
if err := os.WriteFile(configJSONPath, []byte(`{}`), 0600); err != nil {
return "", fmt.Errorf("writing %q: %w", configJSONPath, err)
}
return path, nil
}
var subCmdGarageMC = subCmd{
name: "mc",
descr: "Runs the mc (minio-client) binary. The cryptic-net garage can be accessed under the `garage` alias",
descr: "Runs the mc (minio-client) binary. The isle garage can be accessed under the `garage` alias",
checkLock: true,
do: func(subCmdCtx subCmdCtx) error {
@ -17,12 +39,12 @@ var subCmdGarageMC = subCmd{
keyID := flags.StringP(
"key-id", "i", "",
"Optional key ID to use, defaults to that of the shared cryptic-net-global key",
"Optional key ID to use, defaults to that of the shared global key",
)
keySecret := flags.StringP(
"key-secret", "s", "",
"Optional key secret to use, defaults to that of the shared cryptic-net-global key",
"Optional key secret to use, defaults to that of the shared global key",
)
if err := flags.Parse(subCmdCtx.args); err != nil {
@ -50,7 +72,15 @@ var subCmdGarageMC = subCmd{
args = args[i:]
}
args = append([]string{"mc"}, args...)
configDir, err := initMCConfigDir()
if err != nil {
return fmt.Errorf("initializing minio-client config directory: %w", err)
}
args = append([]string{
binPath("mc"),
"--config-dir", configDir,
}, args...)
var (
mcHostVar = fmt.Sprintf(
@ -82,7 +112,7 @@ var subCmdGarageMC = subCmd{
var subCmdGarageCLI = subCmd{
name: "cli",
descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running cryptic-net daemon",
descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running isle daemon",
checkLock: true,
do: func(subCmdCtx subCmdCtx) error {
@ -92,7 +122,7 @@ var subCmdGarageCLI = subCmd{
}
var (
binPath = filepath.Join(envAppDirPath, "bin/garage")
binPath = binPath("garage")
args = append([]string{"garage"}, subCmdCtx.args...)
cliEnv = append(
os.Environ(),
@ -114,7 +144,7 @@ var subCmdGarageCLI = subCmd{
var subCmdGarage = subCmd{
name: "garage",
descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running cryptic-net daemon",
descr: "Runs the garage binary, automatically configured to point to the garage sub-process of a running isle daemon",
do: func(subCmdCtx subCmdCtx) error {
return subCmdCtx.doSubCmd(
subCmdGarageCLI,

@ -2,15 +2,15 @@ package main
import (
"context"
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/garage"
"isle/bootstrap"
"isle/daemon"
"isle/garage"
"fmt"
"net"
"path/filepath"
"strconv"
"code.betamike.com/cryptic-io/pmux/pmuxlib"
"code.betamike.com/micropelago/pmux/pmuxlib"
"github.com/mediocregopher/mediocre-go-lib/v2/mctx"
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
@ -89,7 +89,7 @@ func waitForGarageAndNebula(
// corresponds with the given alloc from the daemon config. This will panic if
// no associated instance can be found.
//
// This assumes that mergeDaemonConfigIntoBootstrap has already been called.
// This assumes that coalesceDaemonConfigAndBootstrap has already been called.
func bootstrapGarageHostForAlloc(
host bootstrap.Host,
alloc daemon.ConfigStorageAllocation,
@ -168,7 +168,7 @@ func garagePmuxProcConfigs(
pmuxProcConfigs = append(pmuxProcConfigs, pmuxlib.ProcessConfig{
Name: fmt.Sprintf("garage-%d", alloc.RPCPort),
Cmd: "garage",
Cmd: binPath("garage"),
Args: []string{"-c", childConfigPath, "server"},
StartAfterFunc: func(ctx context.Context) error {
return waitForNebula(ctx, hostBootstrap)

@ -1,7 +1,7 @@
package main
import (
"cryptic-net/bootstrap"
"isle/bootstrap"
"errors"
"fmt"
"os"

@ -12,7 +12,7 @@ import (
"github.com/mediocregopher/mediocre-go-lib/v2/mlog"
)
// The purpose of this binary is to act as the entrypoint of the cryptic-net
// The purpose of this binary is to act as the entrypoint of the isle
// process. It processes the command-line arguments which are passed in, and
// then passes execution along to an appropriate binary housed in AppDir/bin
// (usually a bash script, which is more versatile than a go program).
@ -27,10 +27,14 @@ func getAppDirPath() string {
var (
envAppDirPath = getAppDirPath()
envRuntimeDirPath = filepath.Join(xdg.RuntimeDir, "cryptic-net")
envDataDirPath = filepath.Join(xdg.DataHome, "cryptic-net")
envRuntimeDirPath = filepath.Join(xdg.RuntimeDir, "isle")
envDataDirPath = filepath.Join(xdg.DataHome, "isle")
)
func binPath(name string) string {
return filepath.Join(envAppDirPath, "bin", name)
}
func main() {
logger := mlog.NewLogger(&mlog.LoggerOpts{
@ -66,6 +70,7 @@ func main() {
subCmdDaemon,
subCmdGarage,
subCmdHosts,
subCmdNebula,
subCmdVersion,
)

@ -0,0 +1,82 @@
package main
import (
"fmt"
"os"
"github.com/slackhq/nebula/cert"
"gopkg.in/yaml.v3"
)
var subCmdNebulaShow = subCmd{
name: "show",
descr: "Writes nebula network information to stdout in yaml format",
do: func(subCmdCtx subCmdCtx) error {
flags := subCmdCtx.flagSet(false)
if err := flags.Parse(subCmdCtx.args); err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
hostBootstrap, err := loadHostBootstrap()
if err != nil {
return fmt.Errorf("loading host bootstrap: %w", err)
}
caPublicCreds := hostBootstrap.Nebula.CAPublicCredentials
caCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(caPublicCreds.CertPEM))
if err != nil {
return fmt.Errorf("unmarshaling ca.crt: %w", err)
}
if len(caCert.Details.Subnets) != 1 {
return fmt.Errorf(
"malformed ca.crt, contains unexpected subnets %#v",
caCert.Details.Subnets,
)
}
subnet := caCert.Details.Subnets[0]
type outLighthouse struct {
PublicAddr string `yaml:"public_addr"`
IP string `yaml:"ip"`
}
out := struct {
CACert string `yaml:"ca_cert_pem"`
SubnetCIDR string `yaml:"subnet_cidr"`
Lighthouses []outLighthouse `yaml:"lighthouses"`
}{
CACert: caPublicCreds.CertPEM,
SubnetCIDR: subnet.String(),
}
for _, h := range hostBootstrap.Hosts {
if h.Nebula.PublicAddr == "" {
continue
}
out.Lighthouses = append(out.Lighthouses, outLighthouse{
PublicAddr: h.Nebula.PublicAddr,
IP: h.IP().String(),
})
}
if err := yaml.NewEncoder(os.Stdout).Encode(out); err != nil {
return fmt.Errorf("yaml encoding to stdout: %w", err)
}
return nil
},
}
var subCmdNebula = subCmd{
name: "nebula",
descr: "Sub-commands related to the nebula VPN",
do: func(subCmdCtx subCmdCtx) error {
return subCmdCtx.doSubCmd(
subCmdNebulaShow,
)
},
}

@ -2,14 +2,14 @@ package main
import (
"context"
"cryptic-net/bootstrap"
"cryptic-net/daemon"
"cryptic-net/yamlutil"
"fmt"
"isle/bootstrap"
"isle/daemon"
"isle/yamlutil"
"net"
"path/filepath"
"code.betamike.com/cryptic-io/pmux/pmuxlib"
"code.betamike.com/micropelago/pmux/pmuxlib"
)
// waitForNebula waits for the nebula interface to have been started up. It does
@ -60,7 +60,7 @@ func nebulaPmuxProcConfig(
"pki": map[string]string{
"ca": hostBootstrap.Nebula.CAPublicCredentials.CertPEM,
"cert": hostBootstrap.Nebula.HostCredentials.Public.CertPEM,
"key": hostBootstrap.Nebula.HostCredentials.KeyPEM,
"key": hostBootstrap.Nebula.HostCredentials.PrivateKeyPEM,
},
"static_host_map": staticHostMap,
"punchy": map[string]bool{
@ -68,7 +68,7 @@ func nebulaPmuxProcConfig(
"respond": true,
},
"tun": map[string]interface{}{
"dev": "cryptic-net-nebula",
"dev": daemonConfig.VPN.Tun.Device,
},
"firewall": daemonConfig.VPN.Firewall,
}
@ -111,7 +111,7 @@ func nebulaPmuxProcConfig(
return pmuxlib.ProcessConfig{
Name: "nebula",
Cmd: "nebula",
Cmd: binPath("nebula"),
Args: []string{"-config", nebulaYmlPath},
}, nil
}

@ -13,7 +13,7 @@ import (
"github.com/shirou/gopsutil/process"
)
var errDaemonNotRunning = errors.New("no cryptic-net daemon process running")
var errDaemonNotRunning = errors.New("no isle daemon process running")
func lockFilePath() string {
return filepath.Join(envRuntimeDirPath, "lock")
@ -29,7 +29,7 @@ func writeLock() error {
if errors.Is(err, os.ErrExist) {
return fmt.Errorf(
"lock file %q already exists, if the cryptic-net daemon is not already running you can safely delete this file",
"lock file %q already exists, if the isle daemon is not already running you can safely delete this file",
lockFilePath,
)

@ -2,6 +2,10 @@ package daemon
import "strconv"
type ConfigTun struct {
Device string `yaml:"device"`
}
type ConfigFirewall struct {
Conntrack ConfigConntrack `yaml:"conntrack"`
Outbound []ConfigFirewallRule `yaml:"outbound"`
@ -50,6 +54,7 @@ type Config struct {
VPN struct {
PublicAddr string `yaml:"public_addr"`
Firewall ConfigFirewall `yaml:"firewall"`
Tun ConfigTun `yaml:"tun"`
} `yaml:"vpn"`
Storage struct {
Allocations []ConfigStorageAllocation

@ -1,11 +1,11 @@
// Package daemon contains types and functions related specifically to the
// cryptic-net daemon.
// isle daemon.
package daemon
import (
"cryptic-net/yamlutil"
"fmt"
"io"
"isle/yamlutil"
"os"
"path/filepath"

@ -0,0 +1,153 @@
// Package garage contains helper functions and types which are useful for
// setting up garage configs, processes, and deployments.
package garage
import (
"encoding/hex"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
)
const (
// Region is the region which garage is configured with.
Region = "garage"
// GlobalBucket is the name of the global garage bucket which is
// accessible to all hosts in the network.
GlobalBucket = "global-shared"
// ReplicationFactor indicates the replication factor set on the garage
// cluster. We currently only support a factor of 3.
ReplicationFactor = 3
)
func nodeKeyPath(metaDirPath string) string {
return filepath.Join(metaDirPath, "node_key")
}
func nodeKeyPubPath(metaDirPath string) string {
return filepath.Join(metaDirPath, "node_key.pub")
}
func nodeRPCPortPath(metaDirPath string) string {
return filepath.Join(metaDirPath, "isle", "rpc_port")
}
// loadAllocID returns the peer ID (ie the public key) of the node at the given
// meta directory.
func loadAllocID(metaDirPath string) (string, error) {
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
pubKey, err := os.ReadFile(nodeKeyPubPath)
if err != nil {
return "", fmt.Errorf("reading %q: %w", nodeKeyPubPath, err)
}
return hex.EncodeToString(pubKey), nil
}
// InitAlloc initializes the meta directory and keys for a particular
// allocation, if it hasn't been done so already. It returns the peer ID (ie the
// public key) and the rpc port in any case.
func InitAlloc(metaDirPath string, initRPCPort int) (string, int, error) {
initDirFor := func(path string) error {
dir := filepath.Dir(path)
return os.MkdirAll(dir, 0750)
}
var err error
exists := func(path string) bool {
if err != nil {
return false
} else if _, err = os.Stat(path); errors.Is(err, fs.ErrNotExist) {
err = nil
return false
} else if err != nil {
err = fmt.Errorf("checking if %q exists: %w", path, err)
return false
}
return true
}
nodeKeyPath := nodeKeyPath(metaDirPath)
nodeKeyPubPath := nodeKeyPubPath(metaDirPath)
nodeRPCPortPath := nodeRPCPortPath(metaDirPath)
nodeKeyPathExists := exists(nodeKeyPath)
nodeKeyPubPathExists := exists(nodeKeyPubPath)
nodeRPCPortPathExists := exists(nodeRPCPortPath)
if err != nil {
return "", 0, err
} else if nodeKeyPubPathExists != nodeKeyPathExists {
return "", 0, fmt.Errorf("%q or %q exist without the other existing", nodeKeyPath, nodeKeyPubPath)
}
var (
pubKeyStr string
rpcPort int
)
if nodeKeyPathExists {
if pubKeyStr, err = loadAllocID(metaDirPath); err != nil {
return "", 0, fmt.Errorf("reading node public key file: %w", err)
}
} else {
if err := initDirFor(nodeKeyPath); err != nil {
return "", 0, fmt.Errorf("creating directory for %q: %w", nodeKeyPath, err)
}
pubKey, privKey := GeneratePeerKey()
if err := os.WriteFile(nodeKeyPath, privKey, 0400); err != nil {
return "", 0, fmt.Errorf("writing private key to %q: %w", nodeKeyPath, err)
} else if err := os.WriteFile(nodeKeyPubPath, pubKey, 0440); err != nil {
return "", 0, fmt.Errorf("writing public key to %q: %w", nodeKeyPubPath, err)
}
pubKeyStr = hex.EncodeToString(pubKey)
}
if nodeRPCPortPathExists {
if rpcPortStr, err := os.ReadFile(nodeRPCPortPath); err != nil {
return "", 0, fmt.Errorf("reading rpc port from %q: %w", nodeRPCPortPath, err)
} else if rpcPort, err = strconv.Atoi(string(rpcPortStr)); err != nil {
return "", 0, fmt.Errorf("parsing rpc port %q from %q: %w", rpcPortStr, nodeRPCPortPath, err)
}
} else {
if err := initDirFor(nodeRPCPortPath); err != nil {
return "", 0, fmt.Errorf("creating directory for %q: %w", nodeRPCPortPath, err)
}
rpcPortStr := strconv.Itoa(initRPCPort)
if err := os.WriteFile(nodeRPCPortPath, []byte(rpcPortStr), 0440); err != nil {
return "", 0, fmt.Errorf("writing rpc port %q to %q: %w", rpcPortStr, nodeRPCPortPath, err)
}
rpcPort = initRPCPort
}
return pubKeyStr, rpcPort, nil
}

@ -1,9 +1,8 @@
module cryptic-net
module isle
go 1.17
require (
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d
github.com/adrg/xdg v0.4.0
github.com/imdario/mergo v0.3.12
github.com/mediocregopher/mediocre-go-lib/v2 v2.0.0-beta.1.0.20221113151154-07f3889a705b
@ -16,6 +15,7 @@ require (
)
require (
code.betamike.com/micropelago/pmux v0.0.0-20230706154656-fde8c2be7540 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/google/go-cmp v0.5.6 // indirect

@ -1,5 +1,5 @@
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d h1:s6nDTg23o9ujZZnl8ohZBDoG4SqPUyFfvod9DQjwmNU=
code.betamike.com/cryptic-io/pmux v0.0.0-20221025185405-29241f144a2d/go.mod h1:cBuEN/rkaM/GH24uQroX/++qDmte+mLudDUqMt6XJWs=
code.betamike.com/micropelago/pmux v0.0.0-20230706154656-fde8c2be7540 h1:ycD1mEkCbrx3Apr71Q2PKgyycRu8wvwX9J4ZSvjyTtQ=
code.betamike.com/micropelago/pmux v0.0.0-20230706154656-fde8c2be7540/go.mod h1:WlEWacLREVfIQl1IlBjKzuDgL56DFRvyl7YiL5gGP4w=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

@ -32,7 +32,7 @@ type HostPublicCredentials struct {
// need to be present on a particular host. Each file is PEM encoded.
type HostCredentials struct {
Public HostPublicCredentials `yaml:"public"`
KeyPEM string `yaml:"key_pem"`
PrivateKeyPEM string `yaml:"key_pem"` // TODO should be private_key_pem
SigningPrivateKeyPEM string `yaml:"signing_private_key_pem"`
}
@ -51,55 +51,38 @@ type CACredentials struct {
SigningPrivateKeyPEM string `yaml:"signing_private_key_pem"`
}
// NewHostCredentials generates a new key/cert for a nebula host using the CA
// key which will be found in the adminFS.
func NewHostCredentials(
caCreds CACredentials, hostName string, ip net.IP,
// NewHostCertPEM generates and signs a new host certificate containing the
// given public key.
func NewHostCertPEM(
caCreds CACredentials, hostPubPEM string, hostName string, ip net.IP,
) (
HostCredentials, error,
string, error,
) {
// The logic here is largely based on
// https://github.com/slackhq/nebula/blob/v1.4.0/cmd/nebula-cert/sign.go
hostPub, _, err := cert.UnmarshalX25519PublicKey([]byte(hostPubPEM))
if err != nil {
return "", fmt.Errorf("unmarshaling public key PEM: %w", err)
}
caSigningKey, _, err := cert.UnmarshalEd25519PrivateKey([]byte(caCreds.SigningPrivateKeyPEM))
if err != nil {
return HostCredentials{}, fmt.Errorf("unmarshaling ca.key: %w", err)
return "", fmt.Errorf("unmarshaling ca.key: %w", err)
}
caCert, _, err := cert.UnmarshalNebulaCertificateFromPEM([]byte(caCreds.Public.CertPEM))
if err != nil {
return HostCredentials{}, fmt.Errorf("unmarshaling ca.crt: %w", err)
return "", fmt.Errorf("unmarshaling ca.crt: %w", err)
}
issuer, err := caCert.Sha256Sum()
if err != nil {
return HostCredentials{}, fmt.Errorf("getting ca.crt issuer: %w", err)
return "", fmt.Errorf("getting ca.crt issuer: %w", err)
}
expireAt := caCert.Details.NotAfter.Add(-1 * time.Second)
subnet := caCert.Details.Subnets[0]
if !subnet.Contains(ip) {
return HostCredentials{}, fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
}
signingPubKey, signingPrivKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(fmt.Errorf("generating ed25519 key: %w", err))
}
signingPrivKeyPEM := cert.MarshalEd25519PrivateKey(signingPrivKey)
signingPubKeyPEM := cert.MarshalEd25519PublicKey(signingPubKey)
var hostPub, hostKey []byte
{
var pubkey, privkey [32]byte
if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
return HostCredentials{}, fmt.Errorf("reading random bytes to form private key: %w", err)
}
curve25519.ScalarBaseMult(&pubkey, &privkey)
hostPub, hostKey = pubkey[:], privkey[:]
return "", fmt.Errorf("invalid ip %q, not contained by network subnet %q", ip, subnet)
}
hostCert := cert.NebulaCertificate{
@ -118,26 +101,64 @@ func NewHostCredentials(
}
if err := hostCert.CheckRootConstrains(caCert); err != nil {
return HostCredentials{}, fmt.Errorf("validating certificate constraints: %w", err)
return "", fmt.Errorf("validating certificate constraints: %w", err)
}
if err := hostCert.Sign(caSigningKey); err != nil {
return HostCredentials{}, fmt.Errorf("signing host cert with ca.key: %w", err)
return "", fmt.Errorf("signing host cert with ca.key: %w", err)
}
hostCertPEM, err := hostCert.MarshalToPEM()
if err != nil {
return "", fmt.Errorf("marshalling host.crt: %w", err)
}
return string(hostCertPEM), nil
}
// NewHostCredentials generates a new key/cert for a nebula host using the CA
// key which will be found in the adminFS.
func NewHostCredentials(
caCreds CACredentials, hostName string, ip net.IP,
) (
HostCredentials, error,
) {
// The logic here is largely based on
// https://github.com/slackhq/nebula/blob/v1.4.0/cmd/nebula-cert/sign.go
signingPubKey, signingPrivKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
panic(fmt.Errorf("generating ed25519 key: %w", err))
}
signingPrivKeyPEM := cert.MarshalEd25519PrivateKey(signingPrivKey)
signingPubKeyPEM := cert.MarshalEd25519PublicKey(signingPubKey)
var hostPub, hostKey []byte
{
var pubkey, privkey [32]byte
if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
return HostCredentials{}, fmt.Errorf("reading random bytes to form private key: %w", err)
}
curve25519.ScalarBaseMult(&pubkey, &privkey)
hostPub, hostKey = pubkey[:], privkey[:]
}
hostPubPEM := cert.MarshalX25519PublicKey(hostPub)
hostKeyPEM := cert.MarshalX25519PrivateKey(hostKey)
hostCertPEM, err := hostCert.MarshalToPEM()
hostCertPEM, err := NewHostCertPEM(caCreds, string(hostPubPEM), hostName, ip)
if err != nil {
return HostCredentials{}, fmt.Errorf("marshalling host.crt: %w", err)
return HostCredentials{}, fmt.Errorf("creating host certificate: %w", err)
}
return HostCredentials{
Public: HostPublicCredentials{
CertPEM: string(hostCertPEM),
CertPEM: hostCertPEM,
SigningKeyPEM: string(signingPubKeyPEM),
},
KeyPEM: string(hostKeyPEM),
PrivateKeyPEM: string(hostKeyPEM),
SigningPrivateKeyPEM: string(signingPrivKeyPEM),
}, nil
}
@ -159,7 +180,7 @@ func NewCACredentials(domain string, subnet *net.IPNet) (CACredentials, error) {
caCert := cert.NebulaCertificate{
Details: cert.NebulaCertificateDetails{
Name: fmt.Sprintf("%s cryptic-net root cert", domain),
Name: fmt.Sprintf("%s isle root cert", domain),
Subnets: []*net.IPNet{subnet},
NotBefore: now,
NotAfter: expireAt,

@ -1,17 +1,40 @@
{
{ stdenv, fetchurl }:
fetchFromGitHub,
callPackage,
let
}: let
version = "765";
src = fetchFromGitHub {
owner = "matthewbauer";
repo = "nix-bundle";
rev = "223f4ffc4179aa318c34dc873a08cb00090db829";
sha256 = "0pqpx9vnjk9h24h9qlv4la76lh5ykljch6g487b26r1r2s9zg7kh";
};
cpuArch = stdenv.buildPlatform.parsed.cpu.name;
in
srcDir = ./go-appimage;
callPackage "${src}/appimagetool.nix" {}
# https://github.com/probonopd/go-appimage
# The author of go-appimage has set up some crazy continuous integration build
# system with github, which is really cool, except that it doesn't preserve
# any older builds of the project. And it's pretty difficult to build it
# ourselves. So fuck it, just embed the build artifacts directly in this
# project.
src = {
"x86_64" = "${srcDir}/appimagetool-${version}-x86_64.AppImage";
"aarch64" = "${srcDir}/go-appimage/appimagetool-${version}-aarch64.AppImage";
"armv7l" = "${srcDir}/go-appimage/appimagetool-${version}-armhf.AppImage";
"i686" = "${srcDir}/go-appimage/appimagetool-${version}-i686.AppImage";
}."${cpuArch}";
in stdenv.mkDerivation rec {
pname = "go-appimage";
inherit version src;
sourceRoot = "squashfs-root";
unpackPhase = ''
cp $src appimagetool
chmod u+wx appimagetool
./appimagetool --appimage-extract
'';
installPhase = ''
mkdir -p $out
cp -r usr/* $out
'';
}

@ -1,7 +1,6 @@
{
stdenv,
glibcStatic,
}: stdenv.mkDerivation rec {
@ -13,11 +12,7 @@
sha256 = "sha256-rZjTgD32h+W5OAgPPSXGKP5ByHh1LQP7xhmXh/7jEvo=";
};
nativeBuildInputs = [ glibcStatic ];
makeFlags = [
"LDFLAGS=-static"
"DESTDIR="
"BINDIR=$(out)/bin"
"MANDIR=$(out)/man"
"LOCALEDIR=$(out)/share/locale"

@ -1,33 +1,33 @@
{
rec {
fetchgit,
buildEnv,
minio-client,
version = "0.8.1";
}: let
version = "0.8.0-unstable";
src = fetchgit {
src = builtins.fetchGit {
name = "garage-v${version}";
url = "https://git.deuxfleurs.fr/Deuxfleurs/garage.git";
rev = "293139a94a8911aaac1b650e4707379a972196aa";
sha256 = "sha256-b6HHLnxMdmpngiywll6Egr8O9/4cqBN01Mj3OwVMeBc=";
rev = "76230f20282e73a5a5afa33af68152acaf732cf5";
};
in rec {
package = {
pkgsSrc,
buildSystem,
hostSystem,
}: let
garage = (import "${src}/default.nix") { git_version = version; };
compile = (import "${src}/nix/compile.nix") {
minioClient = minio-client;
system = buildSystem;
target = hostSystem;
pkgsSrc = pkgsSrc;
env = buildEnv {
name = "cryptic-net-garage";
paths = [
garage.pkgs.amd64.release
minioClient
];
};
cargo2nixOverlay = (import "${src}/nix/common.nix").cargo2nixOverlay;
}
release = true;
git_version = version;
};
in
compile.workspace.garage {
compileMode = "build";
};
}

@ -2,37 +2,61 @@ rec {
overlays = [
# Make buildGoModules use static compilation by default, and use go 1.18
# everywhere.
(final: prev:
# Make buildGoModules use static compilation by default
(final: prev: let
buildArgs = {
doCheck = false;
CGO_ENABLED=0;
tags = [ "netgo" "timetzdata" ];
ldflags = [ "-w" "-extldflags=-static" ];
};
in {
buildGoModule = args: prev.buildGoModule (buildArgs // args);
})
# for whatever reason git checks fail when flake is being used (or maybe
# it's crossSystem's fault)
(final: prev: {
git = prev.git.overrideAttrs (oldAttrs: {
installCheckPhase = ''
# noop
'';
});
})
let
buildArgs = {
doCheck = false;
CGO_ENABLED=0;
tags = [ "netgo" "timetzdata" ];
ldflags = [ "-w" "-extldflags=-static" ];
};
in {
go = prev.go_1_18;
buildGoModule = args: prev.buildGo118Module (buildArgs // args);
buildGo118Module = args: prev.buildGo118Module (buildArgs // args);
}
)
];
version = "22-05";
rev = "2aec372cdcd4d73b94863611fea70e0884270fdc";
version = "22.11";
rev = "ce20e9ebe1903ea2ba1ab006ec63093020c761cb";
src = fetchTarball {
name = "nixpkgs-${version}";
url = "https://github.com/NixOS/nixpkgs/archive/${rev}.tar.gz";
sha256 = "1pbfhlh4v8l70p44gspsci3i6w0wk70vaisiawg3jhka2nxb8367";
sha256 = "sha256-eFNm2h6fNbgD7ZpO4MHikCB5pSnCJ7DTmwPisjetmwc=";
};
pkgs = import src { inherit overlays; };
supportedSystems = [
"x86_64-linux"
"aarch64-linux"
#"armv7l-linux-musl" # rpi, I think?
"i686-linux"
];
default = {
buildSystem,
hostSystem ? buildSystem,
}: import src ({
inherit overlays;
system = buildSystem;
} // (if buildSystem == hostSystem then {} else {
# The nixpkgs cache doesn't have any packages where cross-compiling has been
# enabled, even if the target platform is actually the same as the build
# platform (and therefore it's not really cross-compiling). So we only set
# up the cross-compiling config if the target platform is different.
crossSystem.config = hostSystem;
}));
}

@ -0,0 +1,51 @@
{
releaseName,
revision,
buildSystem ? builtins.currentSystem,
pkgsNix ? (import ./nix/pkgs.nix),
supportedSystems ? pkgsNix.supportedSystems,
}: let
pkgs = pkgsNix.default { inherit buildSystem; };
mkRelease = hostSystem: let
appImage = ((import ./default.nix) {
inherit buildSystem hostSystem releaseName revision;
}).appImage;
in pkgs.stdenv.mkDerivation {
name = "isle-release-${hostSystem}";
inherit releaseName appImage hostSystem;
builder = builtins.toFile "build.sh" ''
source $stdenv/setup
mkdir -p "$out"/
cp "$appImage"/bin/isle "$out"/isle-$releaseName-$hostSystem
'';
};
releases = builtins.map mkRelease supportedSystems;
in
pkgs.stdenv.mkDerivation {
name = "isle-release-${releaseName}";
inherit releases;
nativeBuildInputs = [ pkgs.coreutils ];
builder = builtins.toFile "build.sh" ''
source $stdenv/setup
mkdir -p "$out"
for p in $releases; do
cp "$p"/isle-* "$out"/
done
(cd "$out" && sha256sum * > sha256.txt)
'';
}

@ -0,0 +1,37 @@
#!/usr/bin/env sh
set -e
scriptDir=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd -P)
cd "$scriptDir"
printf "Release name (e.g. \"v0.1.2\"): "
read -r releaseName
releasesDir="$(pwd)/releases"
mkdir -p "$releasesDir"
echo '*' > "$releasesDir"/.gitignore
out="$releasesDir/$releaseName"
if [ -e "$out" ]; then
echo "$out already exists, aborting"
exit 1
fi
revision=$(git rev-parse HEAD)
result=$(nix-build \
--argstr revision "$revision" \
--argstr releaseName "$releaseName" \
--no-out-link \
release.nix \
)
cp -rL "$result" "$out"
chmod u+w -R "$out"
cd "$out"
gpg -a --detach-sign -o sha256.txt.gpg ./sha256.txt
echo "Release successfully created: $out"

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -e
entrypoint="$(nix-build --no-out-link -A tests)"
this_user="$(whoami)"
echo "Requesting sudo in order to set thread capabilities, will drop back down to user '$this_user' immediately"
sudo -E capsh \
--caps="cap_net_admin,cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep" \
--keep=1 \
--user="$this_user" \
--addamb=cap_net_admin \
--addamb=cap_net_bind_service \
-- "$entrypoint" "$@"

@ -0,0 +1,10 @@
Ctrl+A X -> exits
qemu-system-aarch64 -nographic -cdrom tests/alpine-virt-3.18.4-aarch64.iso
Ctrl+Alt+G -> Escape mouse capture
qemu-system-x86_64 \
-cdrom tests/virt/Win11_23H2_English_x64.iso \
-m 8G \
-boot order=d \
-drive file=./tests/virt/winblows.qcow2

@ -0,0 +1,3 @@
isle version | grep -q 'Release:'
isle version | grep -q 'Platform:'
isle version | grep -q 'Build Platform:'

@ -0,0 +1,16 @@
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
[ "$(cat a/meta/isle/rpc_port)" = "3900" ]
[ "$(cat b/meta/isle/rpc_port)" = "3910" ]
[ "$(cat c/meta/isle/rpc_port)" = "3920" ]
[ "$(yq <admin.yml '.creation_params.id')" != "" ]
[ "$(yq <admin.yml '.creation_params.name')" = "testing" ]
[ "$(yq <admin.yml '.creation_params.domain')" = "shared.test" ]
bootstrap_file="$XDG_DATA_HOME/isle/bootstrap.yml"
[ "$(yq <"$bootstrap_file" '.admin_creation_params')" = "$(yq <admin.yml '.creation_params')" ]
[ "$(yq <"$bootstrap_file" '.nebula.ca_public_credentials')" = "$(yq <admin.yml '.nebula.ca_credentials.public')" ]
[ "$(yq <"$bootstrap_file" '.hostname')" = "primus" ]

@ -0,0 +1,14 @@
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
adminBS="$XDG_DATA_HOME"/isle/bootstrap.yml
bs="$secondus_bootstrap" # set in with-1-data-1-empty-node-cluster.sh
[ "$(yq <"$bs" '.admin_creation_params')" = "$(yq <admin.yml '.creation_params')" ]
[ "$(yq <"$bs" '.hostname')" = "secondus" ]
[ "$(yq <"$bs" '.hosts.primus.nebula.signed_public_credentials')" \
= "$(yq <"$adminBS" '.nebula.signed_public_credentials')" ]
[ "$(yq <"$bs" '.hosts.primus.garage.instances|length')" = "3" ]

@ -0,0 +1,20 @@
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
function assert_a {
want_ip="$1"
hostname="$2"
r="$(dig @"$current_ip" +noall +answer "$hostname")"
echo "$r" | grep -q "$want_ip"
}
as_primus
assert_a "$primus_ip" primus.hosts.shared.test
# TODO This doesn't work at present, there would need to be some mechanism to
# block the test until secondus' bootstrap info can propagate to primus.
#assert_a "$secondus_ip" secondus.hosts.shared.test
as_secondus
assert_a "$primus_ip" primus.hosts.shared.test
assert_a "$secondus_ip" secondus.hosts.shared.test

@ -0,0 +1,21 @@
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
function do_tests {
status="$(isle garage cli status | tail -n+3)"
[ "$(echo "$status" | wc -l)" = "3" ]
echo "$status" | grep -q '10.6.9.1:3900'
echo "$status" | grep -q '10.6.9.1:3910'
echo "$status" | grep -q '10.6.9.1:3920'
buckets="$(isle garage cli bucket list | tail -n+2)"
[ "$(echo "$buckets" | wc -l)" = 1 ]
echo "$buckets" | grep -q 'global-shared'
}
as_primus
do_tests
as_secondus
do_tests

@ -0,0 +1,16 @@
# shellcheck source=../../utils/with-1-data-1-empty-node-cluster.sh
source "$UTILS"/with-1-data-1-empty-node-cluster.sh
function do_tests {
files="$(isle garage mc -- tree --json garage)"
[ "$(echo "$files" | jq -s '.|length')" -ge "1" ]
file="$(echo "$files" | jq -sr '.[0].key')"
[ "$(isle garage mc -- cat "garage/$file" | wc -c)" -gt "0" ]
}
as_primus
do_tests
as_secondus
do_tests

@ -0,0 +1,114 @@
set -e
# cd into script's directory
cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null
root=$(pwd)
export UTILS="$root"/utils
REGEXS=()
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
cat <<EOF
USAGE: [flags] [test regexs...]
FLAGS
--keep-tmp
--verbose (-v)
--help (-h)
EOF
exit 1
;;
-v|--verbose)
VERBOSE=1
shift
;;
--keep-tmp)
KEEP_TMP=1
shift
;;
*)
REGEXS+=("$1")
shift
;;
esac
done
[ -n "$VERBOSE" ] && set -x
ROOT_TMPDIR="$(mktemp --tmpdir -d isle-tests.XXXXXX)"
if [ -z "$KEEP_TMP" ]; then trap 'rm -rf $ROOT_TMPDIR' EXIT; fi
TMPDIR="$ROOT_TMPDIR"
export ROOT_TMPDIR TMPDIR
echo "tmp dir is $ROOT_TMPDIR"
# Blackhole these directories so that tests don't accidentally use the host's
# real ones.
export XDG_RUNTIME_DIR=/dev/null
export XDG_DATA_HOME=/dev/null
test_files=$(
find ./cases -type f -name '*.sh' \
| sed "s|^\./cases/||" \
| grep -v entrypoint.sh \
| sort
)
for r in "${REGEXS[@]}"; do
test_files="$(echo "$test_files" | grep "$r")"
done
echo -e "number of tests: $(echo "$test_files" | wc -l)\n"
for file in $test_files; do
echo "Running test case: $file"
if [ -z "$VERBOSE" ]; then
output="$TMPDIR/$file.log"
mkdir -p "$(dirname "$output")"
else
output=/dev/stdout
fi
(
export TEST_CASE_FILE="$file"
if ! $SHELL -e -x "$root/cases/$file" >"$output" 2>&1; then
echo "$file FAILED"
if [ -z "$VERBOSE" ]; then
echo "output of test is as follows"
echo "------------------------------"
cat "$output"
echo "------------------------------"
fi
exit 1
fi
) || TESTS_FAILED=1
if [ -n "$TESTS_FAILED" ]; then break; fi
done
# Clean up any shared running clusters. Each cleanup script is responsible for
# figuring out if its shared cluster was actually instantiated during any tests.
if [ -e "$ROOT_TMPDIR/cleanup-pids" ]; then
echo "Cleaning up running pids"
tac "$ROOT_TMPDIR/cleanup-pids" | while read -r line; do
pid="$(echo "$line" | cut -d' ' -f1)"
descr="$(echo "$line" | cut -d' ' -f2-)"
echo "Killing $descr ($pid)"
kill "$pid"
done
# This is easier than checking if the pids are still running, and for some
# reason it doesn't occur until after the pids have died anyway
echo "Waiting for appimage mounts to unmount"
while [ "$(find "$ROOT_TMPDIR" -type d -name '*.mount_isle*' | wc -l)" -ge "1" ]; do
sleep 1
done
fi
if [ -z "$TESTS_FAILED" ]; then echo -e '\nall tests succeeded!'; fi

@ -0,0 +1,3 @@
set -e
echo "$1" "$2" >> "$ROOT_TMPDIR/cleanup-pids"

@ -0,0 +1,16 @@
set -e
base="$1"
TMPDIR="$ROOT_TMPDIR/$base"
XDG_RUNTIME_DIR="$TMPDIR/.run"
XDG_DATA_HOME="$TMPDIR/.data"
mkdir -p "$TMPDIR" "$XDG_RUNTIME_DIR" "$XDG_DATA_HOME"
cat <<EOF
export TMPDIR="$TMPDIR"
export XDG_RUNTIME_DIR="$XDG_RUNTIME_DIR"
export XDG_DATA_HOME="$XDG_DATA_HOME"
cd "$TMPDIR"
EOF

@ -0,0 +1,93 @@
set -e
base="shared/1-data-1-empty"
primus_base="$base/primus"
primus_ip="10.6.9.1"
secondus_base="$base/secondus"
secondus_ip="10.6.9.2"
function as_primus {
current_ip="$primus_ip"
eval "$($SHELL "$UTILS/shared-daemon-env.sh" "$primus_base")"
}
function as_secondus {
current_ip="$secondus_ip"
eval "$($SHELL "$UTILS/shared-daemon-env.sh" "$secondus_base")"
}
# Even if it's already intialized, we want to put the caller in primus'
# environment
as_primus
secondus_bootstrap="$(pwd)/secondus-bootstrap.yml"
if [ ! -d "$XDG_RUNTIME_DIR/isle" ]; then
echo "Initializing shared single node cluster"
mkdir a
mkdir b
mkdir c
cat >daemon.yml <<EOF
vpn:
public_addr: 127.0.0.1:60000
tun:
device: isle-primus
storage:
allocations:
- data_path: a/data
meta_path: a/meta
capacity: 100
- data_path: b/data
meta_path: b/meta
capacity: 100
- data_path: c/data
meta_path: c/meta
capacity: 100
EOF
echo "Creating 1-data-1-empty network"
isle admin create-network \
--config-path daemon.yml \
--domain shared.test \
--hostname primus \
--ip-net "$current_ip/24" \
--name "testing" \
> admin.yml
isle daemon --config-path daemon.yml >daemon.log 2>&1 &
pid="$!"
echo "Waiting for primus daemon (process $pid) to initialize"
while ! isle hosts list >/dev/null; do sleep 1; done
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/primus"
echo "Creating secondus bootstrap"
isle admin create-bootstrap \
--admin-path admin.yml \
--hostname secondus \
--ip "$secondus_ip" \
> "$secondus_bootstrap"
(
as_secondus
cat >daemon.yml <<EOF
vpn:
tun:
device: isle-secondus
EOF
isle daemon -c daemon.yml -b "$secondus_bootstrap" >daemon.log 2>&1 &
pid="$!"
echo "Waiting for secondus daemon (process $!) to initialize"
while ! isle hosts list >/dev/null; do sleep 1; done
$SHELL "$UTILS/register-cleanup.sh" "$pid" "1-data-1-empty-node-cluster/secondus"
)
fi

@ -0,0 +1,9 @@
set -e
TMPDIR="$TMPDIR/$TEST_CASE_FILE.tmp"
XDG_RUNTIME_DIR="$TMPDIR/.run"
XDG_DATA_HOME="$TMPDIR/.data"
mkdir -p "$TMPDIR" "$XDG_RUNTIME_DIR" "$XDG_DATA_HOME"
cd "$TMPDIR"
Loading…
Cancel
Save