diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index b57a4b134..107322995 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -31,6 +31,7 @@ body: label: Version description: What version are you running? options: + - v0.21.1 - v0.21.0 - v0.20.6 - v0.20.5 diff --git a/.github/workflows/branchtest.yml b/.github/workflows/branchtest.yml index ed545dee2..d2b22a051 100644 --- a/.github/workflows/branchtest.yml +++ b/.github/workflows/branchtest.yml @@ -25,7 +25,7 @@ jobs: netclientbranch: ${{ steps.getbranch.outputs.netclientbranch }} steps: - name: checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: gravitl/netclient ref: develop diff --git a/.github/workflows/docker-builder.yml b/.github/workflows/docker-builder.yml index d68143371..69373f882 100644 --- a/.github/workflows/docker-builder.yml +++ b/.github/workflows/docker-builder.yml @@ -11,16 +11,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: SetUp Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to Dockerhub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push to docker hub - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: true diff --git a/.github/workflows/publish-docker.yml b/.github/workflows/publish-docker.yml index fcf709ffd..889bcd8a3 100644 --- a/.github/workflows/publish-docker.yml +++ b/.github/workflows/publish-docker.yml @@ -29,22 +29,22 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64, linux/arm64, linux/arm/v7 @@ -69,22 +69,22 @@ jobs: echo "TAG=${TAG}" >> $GITHUB_ENV - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . platforms: linux/amd64, linux/arm64 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 072f07b24..3536088de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v4 with: @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup go uses: actions/setup-go@v4 with: @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v4 with: @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Go uses: actions/setup-go@v4 with: diff --git a/Dockerfile b/Dockerfile index 1c5cefaad..f6df8e6f5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ COPY . . RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} . # RUN go build -tags=ee . -o netmaker main.go -FROM alpine:3.18.3 +FROM alpine:3.18.4 # add a c lib # set the working directory diff --git a/Dockerfile-quick b/Dockerfile-quick index 9502f335c..cf53bdba5 100644 --- a/Dockerfile-quick +++ b/Dockerfile-quick @@ -1,5 +1,5 @@ #first stage - builder -FROM alpine:3.18.3 +FROM alpine:3.18.4 ARG version WORKDIR /app COPY ./netmaker /root/netmaker diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..1aeafe3f4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,203 @@ +Copyright (c) 2023 Netmaker,Inc. + +Portions of this software are licensed as follows: + +* All content that resides under the "pro/" directory of this repository, if that directory exists, is licensed under the license defined in "pro/LICENSE". +* All third party components incorporated into the Netmaker Software are licensed under the original license provided by the owner of the applicable component. +* Content outside of the above mentioned directories or restrictions above is available under the "Apache Version 2.0" license as defined below. + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [2023] Netmaker,Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 4e1383df1..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,557 +0,0 @@ - Server Side Public License - VERSION 1, OCTOBER 16, 2018 - - Copyright © 2018 MongoDB, Inc. - - Everyone is permitted to copy and distribute verbatim copies of this - license document, but changing it is not allowed. - - TERMS AND CONDITIONS - - 0. Definitions. - - “This License” refers to Server Side 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, subject to section 13. 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. Subject to section 13, 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 use, - propagate or 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 use, propagate or 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. Offering the Program as a Service. - - If you make the functionality of the Program or a modified version - available to third parties as a service, you must make the Service Source - Code available via network download to everyone at no charge, under the - terms of this License. Making the functionality of the Program or - modified version available to third parties as a service includes, - without limitation, enabling third parties to interact with the - functionality of the Program or modified version remotely through a - computer network, offering a service the value of which entirely or - primarily derives from the value of the Program or modified version, or - offering a service that accomplishes for users the primary purpose of the - Program or modified version. - - “Service Source Code” means the Corresponding Source for the Program or - the modified version, and the Corresponding Source for all programs that - you use to make the Program or modified version available as a service, - including, without limitation, management software, user interfaces, - application program interfaces, automation software, monitoring software, - backup software, storage software and hosting software, all such that a - user could run an instance of the service using the Service Source Code - you make available. - - 14. Revised Versions of this License. - - MongoDB, Inc. may publish revised and/or new versions of the Server Side - 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 Server Side 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 MongoDB, Inc. If the Program does not - specify a version number of the Server Side Public License, you may - choose any version ever published by MongoDB, Inc. - - If the Program specifies that a proxy can decide which future versions of - the Server Side 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 diff --git a/README.md b/README.md index 6ddb8b670..7976720b4 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@

- + @@ -113,4 +113,11 @@ After installing Netmaker, check out the [Walkthrough](https://itnext.io/getting ## License -Netmaker's source code and all artifacts in this repository are freely available. All versions are published under the Server Side Public License (SSPL), version 1, which can be found here: [LICENSE.txt](./LICENSE.txt). +Netmaker's source code and all artifacts in this repository are freely available. +All content that resides under the "pro/" directory of this repository, if that +directory exists, is licensed under the license defined in "pro/LICENSE". +All third party components incorporated into the Netmaker Software are licensed +under the original license provided by the owner of the applicable component. +Content outside of the above mentioned directories or restrictions above is +available under the "Apache Version 2.0" license as defined below. +All details for the licenses used can be found here: [LICENSE.md](./LICENSE.md). diff --git a/auth/auth.go b/auth/auth.go index 7be57345a..61bbdda10 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -136,6 +136,8 @@ func HandleAuthCallback(w http.ResponseWriter, r *http.Request) { // // Security: // oauth +// Responses: +// 200: okResponse func HandleAuthLogin(w http.ResponseWriter, r *http.Request) { if auth_provider == nil { handleOauthNotConfigured(w) diff --git a/cli/cmd/host/delete.go b/cli/cmd/host/delete.go index 4da8acb0c..b4639de3a 100644 --- a/cli/cmd/host/delete.go +++ b/cli/cmd/host/delete.go @@ -5,16 +5,19 @@ import ( "github.com/spf13/cobra" ) +var force bool + var hostDeleteCmd = &cobra.Command{ Use: "delete HostID", Args: cobra.ExactArgs(1), Short: "Delete a host", Long: `Delete a host`, Run: func(cmd *cobra.Command, args []string) { - functions.PrettyPrint(functions.DeleteHost(args[0])) + functions.PrettyPrint(functions.DeleteHost(args[0], force)) }, } func init() { rootCmd.AddCommand(hostDeleteCmd) + hostDeleteCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "delete even if part of network(s)") } diff --git a/cli/cmd/host/update.go b/cli/cmd/host/update.go index 87fdb9fe4..0809f2d45 100644 --- a/cli/cmd/host/update.go +++ b/cli/cmd/host/update.go @@ -5,9 +5,10 @@ import ( "log" "os" + "github.com/spf13/cobra" + "github.com/gravitl/netmaker/cli/functions" "github.com/gravitl/netmaker/models" - "github.com/spf13/cobra" ) var ( @@ -18,6 +19,7 @@ var ( mtu int isStatic bool isDefault bool + keepAlive int ) var hostUpdateCmd = &cobra.Command{ @@ -43,6 +45,7 @@ var hostUpdateCmd = &cobra.Command{ apiHost.MTU = mtu apiHost.IsStatic = isStatic apiHost.IsDefault = isDefault + apiHost.PersistentKeepalive = keepAlive } functions.PrettyPrint(functions.UpdateHost(args[0], apiHost)) }, @@ -54,6 +57,7 @@ func init() { hostUpdateCmd.Flags().StringVar(&name, "name", "", "Host name") hostUpdateCmd.Flags().IntVar(&listenPort, "listen_port", 0, "Listen port of the host") hostUpdateCmd.Flags().IntVar(&mtu, "mtu", 0, "Host MTU size") + hostUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval (seconds) in which packets are sent to keep connections open with peers") hostUpdateCmd.Flags().BoolVar(&isStatic, "static", false, "Make Host Static ?") hostUpdateCmd.Flags().BoolVar(&isDefault, "default", false, "Make Host Default ?") rootCmd.AddCommand(hostUpdateCmd) diff --git a/cli/cmd/node/delete.go b/cli/cmd/node/delete.go index 501dd7028..4d05a67d1 100644 --- a/cli/cmd/node/delete.go +++ b/cli/cmd/node/delete.go @@ -5,16 +5,19 @@ import ( "github.com/spf13/cobra" ) +var force bool + var nodeDeleteCmd = &cobra.Command{ Use: "delete [NETWORK NAME] [NODE ID]", Args: cobra.ExactArgs(2), Short: "Delete a Node", Long: `Delete a Node`, Run: func(cmd *cobra.Command, args []string) { - functions.PrettyPrint(functions.DeleteNode(args[0], args[1])) + functions.PrettyPrint(functions.DeleteNode(args[0], args[1], force)) }, } func init() { rootCmd.AddCommand(nodeDeleteCmd) + nodeDeleteCmd.PersistentFlags().BoolVarP(&force, "force", "f", false, "force delete a node") } diff --git a/cli/cmd/node/flags.go b/cli/cmd/node/flags.go index 48f2f7495..2ed805d75 100644 --- a/cli/cmd/node/flags.go +++ b/cli/cmd/node/flags.go @@ -11,7 +11,6 @@ var ( name string postUp string postDown string - keepAlive int relayedNodes string egressGatewayRanges string expirationDateTime int diff --git a/cli/cmd/node/uncordon.go b/cli/cmd/node/uncordon.go deleted file mode 100644 index b9b094c2d..000000000 --- a/cli/cmd/node/uncordon.go +++ /dev/null @@ -1,22 +0,0 @@ -package node - -import ( - "fmt" - - "github.com/gravitl/netmaker/cli/functions" - "github.com/spf13/cobra" -) - -var nodeUncordonCmd = &cobra.Command{ - Use: "uncordon [NETWORK NAME] [NODE ID]", - Args: cobra.ExactArgs(2), - Short: "Get a node by ID", - Long: `Get a node by ID`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println(*functions.UncordonNode(args[0], args[1])) - }, -} - -func init() { - rootCmd.AddCommand(nodeUncordonCmd) -} diff --git a/cli/cmd/node/update.go b/cli/cmd/node/update.go index e2d2d3836..b8ff74d07 100644 --- a/cli/cmd/node/update.go +++ b/cli/cmd/node/update.go @@ -34,7 +34,6 @@ var nodeUpdateCmd = &cobra.Command{ node.Address = address node.Address6 = address6 node.LocalAddress = localAddress - node.PersistentKeepalive = int32(keepAlive) if relayedNodes != "" { node.RelayedNodes = strings.Split(relayedNodes, ",") } @@ -61,7 +60,6 @@ func init() { nodeUpdateCmd.Flags().StringVar(&name, "name", "", "Node name") nodeUpdateCmd.Flags().StringVar(&postUp, "post_up", "", "Commands to run after node is up `;` separated") nodeUpdateCmd.Flags().StringVar(&postDown, "post_down", "", "Commands to run after node is down `;` separated") - nodeUpdateCmd.Flags().IntVar(&keepAlive, "keep_alive", 0, "Interval in which packets are sent to keep connections open with peers") nodeUpdateCmd.Flags().StringVar(&relayedNodes, "relayed_nodes", "", "relayed nodes if node acts as a relay") nodeUpdateCmd.Flags().StringVar(&egressGatewayRanges, "egress_addrs", "", "Addresses for egressing traffic if node acts as an egress") nodeUpdateCmd.Flags().IntVar(&expirationDateTime, "expiry", 0, "UNIX timestamp after which node will lose access to the network") diff --git a/cli/functions/host.go b/cli/functions/host.go index bfcf0f03b..04346829f 100644 --- a/cli/functions/host.go +++ b/cli/functions/host.go @@ -17,8 +17,8 @@ func GetHosts() *[]models.ApiHost { } // DeleteHost - delete a host -func DeleteHost(hostID string) *models.ApiHost { - return request[models.ApiHost](http.MethodDelete, "/api/hosts/"+hostID, nil) +func DeleteHost(hostID string, force bool) *models.ApiHost { + return request[models.ApiHost](http.MethodDelete, fmt.Sprintf("/api/hosts/%s?force=%t", hostID, force), nil) } // UpdateHost - update a host diff --git a/cli/functions/node.go b/cli/functions/node.go index ef75ce395..50eed3572 100644 --- a/cli/functions/node.go +++ b/cli/functions/node.go @@ -27,8 +27,8 @@ func UpdateNode(networkName, nodeID string, node *models.ApiNode) *models.ApiNod } // DeleteNode - delete a node -func DeleteNode(networkName, nodeID string) *models.SuccessResponse { - return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s", networkName, nodeID), nil) +func DeleteNode(networkName, nodeID string, force bool) *models.SuccessResponse { + return request[models.SuccessResponse](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s?force=%t", networkName, nodeID, force), nil) } // CreateEgress - turn a node into an egress @@ -52,8 +52,3 @@ func CreateIngress(networkName, nodeID string, failover bool) *models.ApiNode { func DeleteIngress(networkName, nodeID string) *models.ApiNode { return request[models.ApiNode](http.MethodDelete, fmt.Sprintf("/api/nodes/%s/%s/deleteingress", networkName, nodeID), nil) } - -// UncordonNode - uncordon a node -func UncordonNode(networkName, nodeID string) *string { - return request[string](http.MethodPost, fmt.Sprintf("/api/nodes/%s/%s/approve", networkName, nodeID), nil) -} diff --git a/compose/docker-compose.netclient.yml b/compose/docker-compose.netclient.yml index 8c184fffb..e0901f84b 100644 --- a/compose/docker-compose.netclient.yml +++ b/compose/docker-compose.netclient.yml @@ -3,7 +3,7 @@ version: "3.4" services: netclient: container_name: netclient - image: 'gravitl/netclient:v0.21.0' + image: 'gravitl/netclient:v0.21.1' hostname: netmaker-1 network_mode: host restart: on-failure diff --git a/compose/docker-compose.yml b/compose/docker-compose.yml index 7fbf71136..33d9b090c 100644 --- a/compose/docker-compose.yml +++ b/compose/docker-compose.yml @@ -53,7 +53,6 @@ services: - "host.docker.internal:host-gateway" volumes: - ./Caddyfile:/etc/caddy/Caddyfile - - ./certs:/root/certs - caddy_data:/data - caddy_conf:/config ports: diff --git a/config/config.go b/config/config.go index 20a55b253..bf22666c3 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ package config import ( "fmt" "os" + "time" "gopkg.in/yaml.v3" ) @@ -32,62 +33,64 @@ type EnvironmentConfig struct { // ServerConfig - server conf struct type ServerConfig struct { - CoreDNSAddr string `yaml:"corednsaddr"` - APIConnString string `yaml:"apiconn"` - APIHost string `yaml:"apihost"` - APIPort string `yaml:"apiport"` - Broker string `yam:"broker"` - ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"` - BrokerType string `yaml:"brokertype"` - EmqxRestEndpoint string `yaml:"emqxrestendpoint"` - NetclientAutoUpdate string `yaml:"netclientautoupdate"` - NetclientEndpointDetection string `yaml:"netclientendpointdetection"` - MasterKey string `yaml:"masterkey"` - DNSKey string `yaml:"dnskey"` - AllowedOrigin string `yaml:"allowedorigin"` - NodeID string `yaml:"nodeid"` - RestBackend string `yaml:"restbackend"` - MessageQueueBackend string `yaml:"messagequeuebackend"` - DNSMode string `yaml:"dnsmode"` - DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` - Version string `yaml:"version"` - SQLConn string `yaml:"sqlconn"` - Platform string `yaml:"platform"` - Database string `yaml:"database"` - Verbosity int32 `yaml:"verbosity"` - AuthProvider string `yaml:"authprovider"` - OIDCIssuer string `yaml:"oidcissuer"` - ClientID string `yaml:"clientid"` - ClientSecret string `yaml:"clientsecret"` - FrontendURL string `yaml:"frontendurl"` - DisplayKeys string `yaml:"displaykeys"` - AzureTenant string `yaml:"azuretenant"` - Telemetry string `yaml:"telemetry"` - HostNetwork string `yaml:"hostnetwork"` - Server string `yaml:"server"` - PublicIPService string `yaml:"publicipservice"` - MQPassword string `yaml:"mqpassword"` - MQUserName string `yaml:"mqusername"` - MetricsExporter string `yaml:"metrics_exporter"` - BasicAuth string `yaml:"basic_auth"` - LicenseValue string `yaml:"license_value"` - NetmakerTenantID string `yaml:"netmaker_tenant_id"` - IsPro string `yaml:"is_ee" json:"IsEE"` - StunPort int `yaml:"stun_port"` - StunList string `yaml:"stun_list"` - TurnServer string `yaml:"turn_server"` - TurnApiServer string `yaml:"turn_api_server"` - TurnPort int `yaml:"turn_port"` - TurnUserName string `yaml:"turn_username"` - TurnPassword string `yaml:"turn_password"` - UseTurn bool `yaml:"use_turn"` - UsersLimit int `yaml:"user_limit"` - NetworksLimit int `yaml:"network_limit"` - MachinesLimit int `yaml:"machines_limit"` - IngressesLimit int `yaml:"ingresses_limit"` - EgressesLimit int `yaml:"egresses_limit"` - DeployedByOperator bool `yaml:"deployed_by_operator"` - Environment string `yaml:"environment"` + CoreDNSAddr string `yaml:"corednsaddr"` + APIConnString string `yaml:"apiconn"` + APIHost string `yaml:"apihost"` + APIPort string `yaml:"apiport"` + Broker string `yam:"broker"` + ServerBrokerEndpoint string `yaml:"serverbrokerendpoint"` + BrokerType string `yaml:"brokertype"` + EmqxRestEndpoint string `yaml:"emqxrestendpoint"` + NetclientAutoUpdate string `yaml:"netclientautoupdate"` + NetclientEndpointDetection string `yaml:"netclientendpointdetection"` + MasterKey string `yaml:"masterkey"` + DNSKey string `yaml:"dnskey"` + AllowedOrigin string `yaml:"allowedorigin"` + NodeID string `yaml:"nodeid"` + RestBackend string `yaml:"restbackend"` + MessageQueueBackend string `yaml:"messagequeuebackend"` + DNSMode string `yaml:"dnsmode"` + DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` + Version string `yaml:"version"` + SQLConn string `yaml:"sqlconn"` + Platform string `yaml:"platform"` + Database string `yaml:"database"` + Verbosity int32 `yaml:"verbosity"` + AuthProvider string `yaml:"authprovider"` + OIDCIssuer string `yaml:"oidcissuer"` + ClientID string `yaml:"clientid"` + ClientSecret string `yaml:"clientsecret"` + FrontendURL string `yaml:"frontendurl"` + DisplayKeys string `yaml:"displaykeys"` + AzureTenant string `yaml:"azuretenant"` + Telemetry string `yaml:"telemetry"` + HostNetwork string `yaml:"hostnetwork"` + Server string `yaml:"server"` + PublicIPService string `yaml:"publicipservice"` + MQPassword string `yaml:"mqpassword"` + MQUserName string `yaml:"mqusername"` + MetricsExporter string `yaml:"metrics_exporter"` + BasicAuth string `yaml:"basic_auth"` + LicenseValue string `yaml:"license_value"` + NetmakerTenantID string `yaml:"netmaker_tenant_id"` + IsPro string `yaml:"is_ee" json:"IsEE"` + StunPort int `yaml:"stun_port"` + StunList string `yaml:"stun_list"` + TurnServer string `yaml:"turn_server"` + TurnApiServer string `yaml:"turn_api_server"` + TurnPort int `yaml:"turn_port"` + TurnUserName string `yaml:"turn_username"` + TurnPassword string `yaml:"turn_password"` + UseTurn bool `yaml:"use_turn"` + UsersLimit int `yaml:"user_limit"` + NetworksLimit int `yaml:"network_limit"` + MachinesLimit int `yaml:"machines_limit"` + IngressesLimit int `yaml:"ingresses_limit"` + EgressesLimit int `yaml:"egresses_limit"` + DeployedByOperator bool `yaml:"deployed_by_operator"` + Environment string `yaml:"environment"` + JwtValidityDuration time.Duration `yaml:"jwt_validity_duration"` + RacAutoDisable bool `yaml:"rac_auto_disable"` } // SQLConfig - Generic SQL Config diff --git a/controllers/dns.go b/controllers/dns.go index 8d7be2918..8a987a4e8 100644 --- a/controllers/dns.go +++ b/controllers/dns.go @@ -33,6 +33,8 @@ func dnsHandlers(r *mux.Router) { // // Security: // oauth +// Responses: +// 200: dnsResponse func getNodeDNS(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -264,8 +266,8 @@ func GetDNSEntry(domain string, network string) (models.DNSEntry, error) { // oauth // // Responses: -// 200: dnsStringJSONResponse -// *: dnsStringJSONResponse +// 200: dnsResponse +// *: dnsResponse func pushDNS(w http.ResponseWriter, r *http.Request) { // Set header w.Header().Set("Content-Type", "application/json") diff --git a/controllers/dns_test.go b/controllers/dns_test.go index 1e415cc9d..4682f5258 100644 --- a/controllers/dns_test.go +++ b/controllers/dns_test.go @@ -238,7 +238,7 @@ func TestSetDNS(t *testing.T) { assert.False(t, info.IsDir()) content, err := os.ReadFile("./config/dnsconfig/netmaker.hosts") assert.Nil(t, err) - assert.Contains(t, string(content), "linuxhost.skynet") + assert.Contains(t, string(content), "linuxhost") }) t.Run("EntryExists", func(t *testing.T) { entry := models.DNSEntry{Address: "10.0.0.3", Name: "newhost", Network: "skynet"} @@ -251,7 +251,7 @@ func TestSetDNS(t *testing.T) { assert.False(t, info.IsDir()) content, err := os.ReadFile("./config/dnsconfig/netmaker.hosts") assert.Nil(t, err) - assert.Contains(t, string(content), "newhost.skynet") + assert.Contains(t, string(content), "newhost") }) } diff --git a/controllers/docs.go b/controllers/docs.go index 2cbe329ea..491e09039 100644 --- a/controllers/docs.go +++ b/controllers/docs.go @@ -10,8 +10,8 @@ // // Schemes: https // BasePath: / -// Version: 0.21.0 -// Host: netmaker.io +// Version: 0.21.1 +// Host: api.demo.netmaker.io // // Consumes: // - application/json @@ -26,15 +26,37 @@ package controller import ( - serverconfigpkg "github.com/gravitl/netmaker/config" + "os" + + "github.com/gravitl/netmaker/config" "github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/models" ) var _ = useUnused() // "use" the function to prevent "unused function" errors +// swagger:parameters getFile +type filenameToGet struct { + // Filename + // in: path + // required: true + Filename string `json:"filename"` +} + +// swagger:response hasAdmin +type hasAdmin struct { + // in: body + Admin bool +} + +// swagger:response apiHostResponse +type apiHostResponse struct { + // in: body + Host models.ApiHost +} + // swagger:parameters getNodeDNS getCustomDNS getDNS -type dnsPathParams struct { +type dnsNetworkPathParam struct { // Network // in: path Network string `json:"network"` @@ -45,7 +67,6 @@ type dnsParams struct { // Network // in: path Network string `json:"network"` - // DNS Entry // in: body Body []models.DNSEntry `json:"body"` @@ -76,6 +97,18 @@ type stringJSONResponse struct { Response string `json:"response"` } +//swagger:response EnrollmentKey +type EnrollmentKey struct { + // in: body + EnrollmentKey models.EnrollmentKey +} + +//swagger:response EnrollmentKeys +type EnrollmentKeys struct { + // in: body + EnrollmentKeys []models.EnrollmentKey +} + // swagger:parameters getAllExtClients type getAllClientsRequest struct { // Networks @@ -97,6 +130,12 @@ type extClientResponse struct { ExtClient models.ExtClient `json:"ext_client"` } +// swagger:response fileResponse +type fileResponse struct { + // in: body + File os.File +} + // swagger:response successResponse type successResponse struct { // Success Response @@ -104,12 +143,24 @@ type successResponse struct { SuccessResponse models.SuccessResponse `json:"success_response"` } +// swagger:parameters getExtClientConf +type extClientConfParams struct { + // Client ID + // in: path + ClientID string `json:"clientid"` + // Network + // in: path + Network string `json:"network"` + // Type + // in: path + Type string `json:"type"` +} + // swagger:parameters getExtClient getExtClientConf updateExtClient deleteExtClient type extClientPathParams struct { // Client ID // in: path ClientID string `json:"clientid"` - // Network // in: path Network string `json:"network"` @@ -137,20 +188,17 @@ type createExtClientPathParams struct { // Node ID // in: path - NodeID string `json:"node"` + NodeID string `json:"nodeid"` // Custom ExtClient // in: body CustomExtClient models.CustomExtClient `json:"custom_ext_client"` } -// swagger:parameters getNode updateNode deleteNode createRelay deleteRelay createEgressGateway deleteEgressGateway createIngressGateway deleteIngressGateway uncordonNode +// swagger:parameters getNode updateNode deleteNode createRelay deleteRelay createEgressGateway deleteEgressGateway createIngressGateway deleteIngressGateway ingressGatewayUsers type networkNodePathParams struct { - // Network // in: path Network string `json:"network"` - - // Node ID // in: path NodeID string `json:"nodeid"` } @@ -161,11 +209,11 @@ type byteArrayResponse struct { ByteArray []byte `json:"byte_array"` } -// swagger:parameters getNetworks -type headerNetworks struct { - // name: networks - // in: header - Networks []string `json:"networks"` +// swagger:parameters getNetwork deleteNetwork updateNetwork getNetworkACL updateNetworkACL +type NetworkParam struct { + // name: network name + // in: path + Networkname string `json:"networkname"` } // swagger:response getNetworksSliceResponse @@ -175,6 +223,13 @@ type getNetworksSliceResponse struct { Networks []models.Network `json:"networks"` } +// swagger:response hostPull +type hostPull struct { + // hostPull + // in: body + HostPull models.HostPull +} + // swagger:parameters createNetwork updateNetwork type networkBodyParam struct { // Network @@ -182,18 +237,11 @@ type networkBodyParam struct { Network models.Network `json:"network"` } -// swagger:parameters updateNetwork getNetwork updateNetwork updateNetworkNodeLimit deleteNetwork keyUpdate createAccessKey getAccessKeys deleteAccessKey updateNetworkACL getNetworkACL +// swagger:parameters updateNetworkNodeLimit keyUpdate createAccessKey getAccessKeys getNetworkNodes type networkPathParam struct { - // Network Name - // in: path - NetworkName string `json:"networkname"` -} - -// swagger:parameters deleteAccessKey -type networkAccessKeyNamePathParam struct { - // Access Key Name + // Network // in: path - AccessKeyName string `json:"access_key_name"` + Network string `json:"network"` } // swagger:response networkBodyResponse @@ -238,6 +286,15 @@ type nodeBodyParam struct { Node models.LegacyNode `json:"node"` } +//swagger:response okResponse +type okRespone struct{} + +// swagger:response RegisterResponse +type RegisterResponse struct { + // in: body + RegisterResponse models.RegisterResponse +} + // swagger:parameters createRelay type relayRequestBodyParam struct { // Relay Request @@ -252,53 +309,68 @@ type egressGatewayBodyParam struct { EgressGatewayRequest models.EgressGatewayRequest `json:"egress_gateway_request"` } +// swagger:parameters attachUserToRemoteAccessGateway removeUserFromRemoteAccessGW getUserRemoteAccessGws +type RemoteAccessGatewayUser struct { + // in: path + Username string `json:"username"` +} + // swagger:parameters authenticate type authParamBodyParam struct { + // network + // in: path + Network string `json:"network"` // AuthParams // in: body AuthParams models.AuthParams `json:"auth_params"` } -// swagger:response serverConfigResponse -type serverConfigResponse struct { - // Server Config +// swagger:response signal +type signal struct { // in: body - ServerConfig serverconfigpkg.ServerConfig `json:"server_config"` + Signal models.Signal } -// swagger:response nodeGetResponse -type nodeGetResponse struct { - // Node Get - // in: body - NodeGet models.NodeGet `json:"node_get"` +// swagger:parameters synchost deleteHost updateHost signalPeer updateKeys +type HostID struct { + // HostID + // in: path + HostID string `json:"hostid"` } -// swagger:response nodeLastModifiedResponse -type nodeLastModifiedResponse struct { - // Node Last Modified - // in: body - NodesLastModified int64 `json:"nodes_last_modified"` +// swagger:parameters addHostToNetwork deleteHostFromNetwork +type HostFromNetworkParams struct { + // hostid to add or delete from network + // in: path + HostID string `json:"hostid"` + // network + // in: path + Network string `json:"network"` } -// swagger:parameters register -//type registerRequestBodyParam struct { -// // Register Request -// // in: body -// RegisterRequest config.RegisterRequest `json:"register_request"` -//} -// -//// swagger:response registerResponse -//type registerResponse struct { -// // Register Response -// // in: body -// RegisterResponse config.RegisterResponse `json:"register_response"` -//} +// swagger:parameters deleteEnrollmentKey +type DeleteEnrollmentKeyParam struct { + // in: path + KeyID string `json:"keyid"` +} -// swagger:response boolResponse -type boolResponse struct { - // Boolean Response +// swagger:parameters handleHostRegister +type RegisterParams struct { + // in: path + Token string `json:"token"` + // in: body + Host models.Host `json:"host"` +} + +// swagger:response serverConfigResponse +type serverConfigResponse struct { + // Server Config // in: body - BoolResponse bool `json:"bool_response"` + // example + //{ + //"mqusername": "xxxxxxx" + //} + ServerConfig config.ServerConfig `json:"server_config"` } // swagger:parameters createAdmin updateUser updateUserNetworks createUser @@ -331,7 +403,6 @@ type usernamePathParam struct { // prevent issues with integration tests for types just used by Swagger docs. func useUnused() bool { - _ = dnsPathParams{} _ = dnsParams{} _ = dnsResponse{} _ = dnsDeletePathParams{} @@ -346,11 +417,9 @@ func useUnused() bool { _ = createExtClientPathParams{} _ = networkNodePathParams{} _ = byteArrayResponse{} - _ = headerNetworks{} _ = getNetworksSliceResponse{} _ = networkBodyParam{} _ = networkPathParam{} - _ = networkAccessKeyNamePathParam{} _ = networkBodyResponse{} _ = aclContainerBodyParam{} _ = aclContainerResponse{} @@ -361,14 +430,18 @@ func useUnused() bool { _ = egressGatewayBodyParam{} _ = authParamBodyParam{} _ = serverConfigResponse{} - _ = nodeGetResponse{} - _ = nodeLastModifiedResponse{} - // _ = registerRequestBodyParam{} - // _ = registerResponse{} - _ = boolResponse{} _ = userBodyParam{} _ = userBodyResponse{} _ = userAuthBodyParam{} _ = usernamePathParam{} + _ = hasAdmin{} + _ = apiHostResponse{} + _ = fileResponse{} + _ = extClientConfParams{} + _ = hostPull{} + _ = okRespone{} + _ = signal{} + _ = filenameToGet{} + _ = dnsNetworkPathParam{} return false } diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go index 63aca97d4..33b2c7209 100644 --- a/controllers/enrollmentkeys.go +++ b/controllers/enrollmentkeys.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gorilla/mux" + "github.com/gravitl/netmaker/auth" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" @@ -17,10 +18,14 @@ import ( ) func enrollmentKeyHandlers(r *mux.Router) { - r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))).Methods(http.MethodPost) - r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))).Methods(http.MethodGet) - r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).Methods(http.MethodDelete) - r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).Methods(http.MethodPost) + r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))). + Methods(http.MethodPost) + r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))). + Methods(http.MethodGet) + r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))). + Methods(http.MethodDelete) + r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)). + Methods(http.MethodPost) } // swagger:route GET /api/v1/enrollment-keys enrollmentKeys getEnrollmentKeys @@ -33,7 +38,7 @@ func enrollmentKeyHandlers(r *mux.Router) { // oauth // // Responses: -// 200: getEnrollmentKeysSlice +// 200: EnrollmentKeys func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) { keys, err := logic.GetAllEnrollmentKeys() if err != nil { @@ -58,7 +63,7 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(ret) } -// swagger:route DELETE /api/v1/enrollment-keys/{keyID} enrollmentKeys deleteEnrollmentKey +// swagger:route DELETE /api/v1/enrollment-keys/{keyid} enrollmentKeys deleteEnrollmentKey // // Deletes an EnrollmentKey from Netmaker server. // @@ -68,9 +73,9 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: deleteEnrollmentKeyResponse +// 200: okResponse func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) { - var params = mux.Vars(r) + params := mux.Vars(r) keyID := params["keyID"] err := logic.DeleteEnrollmentKey(keyID) if err != nil { @@ -92,9 +97,8 @@ func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: createEnrollmentKeyResponse +// 200: EnrollmentKey func createEnrollmentKey(w http.ResponseWriter, r *http.Request) { - var enrollmentKeyBody models.APIEnrollmentKey err := json.NewDecoder(r.Body).Decode(&enrollmentKeyBody) @@ -109,7 +113,13 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) { newTime = time.Unix(enrollmentKeyBody.Expiration, 0) } - newEnrollmentKey, err := logic.CreateEnrollmentKey(enrollmentKeyBody.UsesRemaining, newTime, enrollmentKeyBody.Networks, enrollmentKeyBody.Tags, enrollmentKeyBody.Unlimited) + newEnrollmentKey, err := logic.CreateEnrollmentKey( + enrollmentKeyBody.UsesRemaining, + newTime, + enrollmentKeyBody.Networks, + enrollmentKeyBody.Tags, + enrollmentKeyBody.Unlimited, + ) if err != nil { logger.Log(0, r.Header.Get("user"), "failed to create enrollment key:", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) @@ -136,9 +146,9 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: handleHostRegisterResponse +// 200: RegisterResponse func handleHostRegister(w http.ResponseWriter, r *http.Request) { - var params = mux.Vars(r) + params := mux.Vars(r) token := params["token"] logger.Log(0, "received registration attempt with token", token) // check if token exists @@ -156,7 +166,6 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - hostExists := false // re-register host with turn just in case. if servercfg.IsUsingTurn() { err = logic.RegisterHostWithTurn(newHost.ID.String(), newHost.HostPass) @@ -165,9 +174,20 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) { } } // check if host already exists + hostExists := false if hostExists = logic.HostExists(&newHost); hostExists && len(enrollmentKey.Networks) == 0 { - logger.Log(0, "host", newHost.ID.String(), newHost.Name, "attempted to re-register with no networks") - logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("host already exists"), "badrequest")) + logger.Log( + 0, + "host", + newHost.ID.String(), + newHost.Name, + "attempted to re-register with no networks", + ) + logic.ReturnErrorResponse( + w, + r, + logic.FormatError(fmt.Errorf("host already exists"), "badrequest"), + ) return } // version check @@ -190,11 +210,16 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) { // use the token if ok := logic.TryToUseEnrollmentKey(enrollmentKey); !ok { logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration") - logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest")) + logic.ReturnErrorResponse( + w, + r, + logic.FormatError(fmt.Errorf("invalid enrollment key"), "badrequest"), + ) return } hostPass := newHost.HostPass if !hostExists { + newHost.PersistentKeepalive = models.DefaultPersistentKeepAlive // register host logic.CheckHostPorts(&newHost) // create EMQX credentials and ACLs for host @@ -209,14 +234,21 @@ func handleHostRegister(w http.ResponseWriter, r *http.Request) { } } if err = logic.CreateHost(&newHost); err != nil { - logger.Log(0, "host", newHost.ID.String(), newHost.Name, "failed registration -", err.Error()) + logger.Log( + 0, + "host", + newHost.ID.String(), + newHost.Name, + "failed registration -", + err.Error(), + ) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } } else { // need to revise the list of networks from key // based on the ones host currently has - var networksToAdd = []string{} + networksToAdd := []string{} currentNets := logic.GetHostNetworks(newHost.ID.String()) for _, newNet := range enrollmentKey.Networks { if !logic.StringSliceContains(currentNets, newNet) { diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 898f5098e..e47c6c9b5 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -90,16 +90,6 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - headerNetworks := r.Header.Get("networks") - networksSlice := []string{} - marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice) - if marshalErr != nil { - slog.Error("error unmarshalling networks", "error", marshalErr.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(marshalErr, "internal")) - return - } - - var err error clients, err := logic.GetAllExtClients() if err != nil && !database.IsEmptyRecord(err) { logger.Log(0, "failed to get all extclients: ", err.Error()) @@ -313,6 +303,8 @@ Endpoint = %s // // Security: // oauth +// Responses: +// 200: okResponse func createExtClient(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -385,6 +377,11 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID extclient.IngressGatewayID = nodeid + // set extclient dns to ingressdns if extclient dns is not explicitly set + if (extclient.DNS == "") && (node.IngressDNS != "") { + extclient.DNS = node.IngressDNS + } + extclient.Network = node.Network host, err := logic.GetHost(node.HostID.String()) if err != nil { diff --git a/controllers/files.go b/controllers/files.go index dfe7a3377..e4e6bf0f3 100644 --- a/controllers/files.go +++ b/controllers/files.go @@ -7,7 +7,7 @@ import ( ) func fileHandlers(r *mux.Router) { - // swagger:route GET /meshclient/files/{filename} meshclient fileServer + // swagger:route GET /meshclient/files/{filename} meshclient getFile // // Retrieve a file from the file server. // @@ -15,5 +15,7 @@ func fileHandlers(r *mux.Router) { // // Security: // oauth + // Responses: + // 200: fileResponse r.PathPrefix("/meshclient/files").Handler(http.StripPrefix("/meshclient/files", http.FileServer(http.Dir("./meshclient/files")))) } diff --git a/controllers/hosts.go b/controllers/hosts.go index 0c6e9fd65..a587db4a0 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -23,6 +23,7 @@ func hostHandlers(r *mux.Router) { r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).Methods(http.MethodPost) r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut) r.HandleFunc("/api/hosts/{hostid}", Authorize(true, false, "all", http.HandlerFunc(deleteHost))).Methods(http.MethodDelete) + r.HandleFunc("/api/hosts/{hostid}/upgrade", logic.SecurityCheck(true, http.HandlerFunc(upgradeHost))).Methods(http.MethodPut) r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost) r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete) r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost) @@ -31,6 +32,22 @@ func hostHandlers(r *mux.Router) { r.HandleFunc("/api/v1/auth-register/host", socketHandler) } +// upgrade host is a handler to send upgrade message to a host +func upgradeHost(w http.ResponseWriter, r *http.Request) { + host, err := logic.GetHost(mux.Vars(r)["hostid"]) + if err != nil { + slog.Error("failed to find host", "error", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "notfound")) + return + } + if err := mq.HostUpdate(&models.HostUpdate{Action: models.Upgrade, Host: *host}); err != nil { + slog.Error("failed to upgrade host", "error", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + logic.ReturnSuccessResponse(w, r, "passed message to upgrade host") +} + // swagger:route GET /api/hosts hosts getHosts // // Lists all hosts. @@ -41,7 +58,7 @@ func hostHandlers(r *mux.Router) { // oauth // // Responses: -// 200: getHostsSliceResponse +// 200: apiHostResponse func getHosts(w http.ResponseWriter, r *http.Request) { currentHosts, err := logic.GetAllHosts() if err != nil { @@ -56,7 +73,7 @@ func getHosts(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(apiHosts) } -// swagger:route GET /api/v1/host pull pullHost +// swagger:route GET /api/v1/host hosts pullHost // // Used by clients for "pull" command // @@ -66,7 +83,7 @@ func getHosts(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: pull +// 200: hostPull func pull(w http.ResponseWriter, r *http.Request) { hostID := r.Header.Get(hostIDHeader) // return JSON/API formatted keys @@ -128,7 +145,7 @@ func pull(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: updateHostResponse +// 200: apiHostResponse func updateHost(w http.ResponseWriter, r *http.Request) { var newHostData models.ApiHost err := json.NewDecoder(r.Body).Decode(&newHostData) @@ -196,7 +213,7 @@ func updateHost(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: deleteHostResponse +// 200: apiHostResponse func deleteHost(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) hostid := params["hostid"] @@ -235,9 +252,8 @@ func deleteHost(w http.ResponseWriter, r *http.Request) { // // Security: // oauth -// // Responses: -// 200: addHostToNetworkResponse +// 200: okResponse func addHostToNetwork(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) @@ -284,7 +300,7 @@ func addHostToNetwork(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: deleteHostFromNetworkResponse +// 200: okResponse func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) @@ -343,9 +359,12 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { } node.Action = models.NODE_DELETE node.PendingDelete = true - // notify node change - mq.RunUpdates(node, false) - go func() { // notify of peer change + go func() { + // notify node change + if err := mq.NodeUpdate(node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + // notify of peer change err = mq.PublishDeletedNodePeerUpdate(node) if err != nil { logger.Log(1, "error publishing peer update ", err.Error()) @@ -358,7 +377,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -// swagger:route POST /api/hosts/adm/authenticate hosts authenticateHost +// swagger:route POST /api/hosts/adm/authenticate authenticate authenticateHost // // Host based authentication for making further API calls. // @@ -451,7 +470,7 @@ func authenticateHost(response http.ResponseWriter, request *http.Request) { response.Write(successJSONResponse) } -// swagger:route POST /api/hosts/{hostid}/signalpeer signalPeer +// swagger:route POST /api/hosts/{hostid}/signalpeer hosts signalPeer // // send signal to peer. // @@ -517,7 +536,7 @@ func signalPeer(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(signal) } -// swagger:route POST /api/hosts/keys host updateAllKeys +// swagger:route POST /api/hosts/keys hosts updateAllKeys // // Update keys for a network. // @@ -555,7 +574,7 @@ func updateAllKeys(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -// swagger:route POST /api/hosts/{hostid}keys host updateKeys +// swagger:route POST /api/hosts/{hostid}keys hosts updateKeys // // Update keys for a network. // @@ -594,7 +613,7 @@ func updateKeys(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } -// swagger:route POST /api/hosts/{hostId}/sync host syncHost +// swagger:route POST /api/hosts/{hostid}/sync hosts synchost // // Requests a host to pull. // diff --git a/controllers/legacy.go b/controllers/legacy.go index d47db645e..a6115be9e 100644 --- a/controllers/legacy.go +++ b/controllers/legacy.go @@ -22,7 +22,7 @@ func legacyHandlers(r *mux.Router) { // oauth // // Responses: -// 200: wipeLegacyNodesResponse +// 200: successResponse func wipeLegacyNodes(w http.ResponseWriter, r *http.Request) { // Set header w.Header().Set("Content-Type", "application/json") diff --git a/controllers/migrate.go b/controllers/migrate.go index cf8089069..148295951 100644 --- a/controllers/migrate.go +++ b/controllers/migrate.go @@ -19,7 +19,7 @@ import ( "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) -// swagger:route PUT /api/v1/nodes/migrate nodes migrateNode +// swagger:route PUT /api/v1/nodes/migrate nodes migrateData // // Used to migrate a legacy node. // @@ -29,7 +29,7 @@ import ( // oauth // // Responses: -// 200: nodeJoinResponse +// 200: hostPull func migrate(w http.ResponseWriter, r *http.Request) { data := models.MigrationData{} host := models.Host{} @@ -65,6 +65,7 @@ func migrate(w http.ResponseWriter, r *http.Request) { host.Name = data.HostName host.HostPass = data.Password host.OS = data.OS + host.PersistentKeepalive = time.Duration(legacy.PersistentKeepalive) if err := logic.CreateHost(&host); err != nil { slog.Error("create host", "error", err) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) @@ -123,7 +124,11 @@ func migrate(w http.ResponseWriter, r *http.Request) { if err != nil { logger.Log(0, "error creating ingress gateway for node", node.ID, err.Error()) } - mq.RunUpdates(&ingressNode, true) + go func() { + if err := mq.NodeUpdate(&ingressNode); err != nil { + slog.Error("error publishing node update to node", "node", ingressNode.ID, "error", err) + } + }() } } } @@ -198,7 +203,6 @@ func convertLegacyNode(legacy models.LegacyNode, hostID uuid.UUID) models.Node { node.IsRelay = false node.RelayedNodes = []string{} node.DNSOn = models.ParseBool(legacy.DNSOn) - node.PersistentKeepalive = time.Duration(int64(time.Second) * int64(legacy.PersistentKeepalive)) node.LastModified = time.Now() node.ExpirationDateTime = time.Unix(legacy.ExpirationDateTime, 0) node.EgressGatewayNatEnabled = models.ParseBool(legacy.EgressGatewayNatEnabled) diff --git a/controllers/network.go b/controllers/network.go index 3e30d07c6..3c8cbf7a3 100644 --- a/controllers/network.go +++ b/controllers/network.go @@ -180,7 +180,7 @@ func getNetworkACL(w http.ResponseWriter, r *http.Request) { // oauth // // Responses: -// 200: stringJSONResponse +// 200: successResponse func deleteNetwork(w http.ResponseWriter, r *http.Request) { // Set header w.Header().Set("Content-Type", "application/json") @@ -278,7 +278,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(network) } -// swagger:route PUT /api/networks networks updateNetwork +// swagger:route PUT /api/networks/{networkname} networks updateNetwork // // Update pro settings for a network. // diff --git a/controllers/node.go b/controllers/node.go index 81693a03b..2051304dd 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -34,7 +34,7 @@ func nodeHandlers(r *mux.Router) { r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) } -// swagger:route POST /api/nodes/adm/{network}/authenticate nodes authenticate +// swagger:route POST /api/nodes/adm/{network}/authenticate authenticate authenticate // // Authenticate to make further API calls related to a network. // @@ -440,9 +440,11 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(apiNode) go func() { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } mq.PublishPeerUpdate() }() - mq.RunUpdates(&node, true) } // swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway @@ -481,9 +483,11 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(apiNode) go func() { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } mq.PublishPeerUpdate() }() - mq.RunUpdates(&node, true) } // == INGRESS == @@ -530,8 +534,11 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) { logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(apiNode) - - mq.RunUpdates(&node, true) + go func() { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + }() } // swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway @@ -582,16 +589,16 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { if err != nil { return } - go mq.PublishSingleHostPeerUpdate( - host, - allNodes, - nil, - removedClients[:], - ) + go func() { + if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:]); err != nil { + slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err) + } + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + }() } } - - mq.RunUpdates(&node, true) } // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode @@ -660,9 +667,11 @@ func updateNode(w http.ResponseWriter, r *http.Request) { logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(apiNode) - mq.RunUpdates(newNode, ifaceDelta) go func(aclUpdate, relayupdate bool, newNode *models.Node) { - if aclUpdate || relayupdate { + if err := mq.NodeUpdate(newNode); err != nil { + slog.Error("error publishing node update to node", "node", newNode.ID, "error", err) + } + if aclUpdate || relayupdate || ifaceDelta { if err := mq.PublishPeerUpdate(); err != nil { logger.Log(0, "error during node ACL update for node", newNode.ID.String()) } @@ -735,13 +744,13 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { logic.ReturnSuccessResponse(w, r, nodeid+" deleted.") logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"]) - if !fromNode { // notify node change - mq.RunUpdates(&node, false) - } go func() { // notify of peer change - var err error - err = mq.PublishDeletedNodePeerUpdate(&node) - if err != nil { + if !fromNode { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + } + if err := mq.PublishDeletedNodePeerUpdate(&node); err != nil { logger.Log(1, "error publishing peer update ", err.Error()) } host, err := logic.GetHost(node.HostID.String()) diff --git a/controllers/server.go b/controllers/server.go index b4d4f6964..d6a10f547 100644 --- a/controllers/server.go +++ b/controllers/server.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/gorilla/mux" + "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" @@ -15,24 +16,32 @@ import ( func serverHandlers(r *mux.Router) { // r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(true, http.HandlerFunc(addNetwork))).Methods(http.MethodPost) - r.HandleFunc("/api/server/health", http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - resp.WriteHeader(http.StatusOK) - resp.Write([]byte("Server is up and running!!")) - })) - r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))).Methods(http.MethodGet) - r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))).Methods(http.MethodGet) + r.HandleFunc( + "/api/server/health", + http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(http.StatusOK) + resp.Write([]byte("Server is up and running!!")) + }), + ) + r.HandleFunc("/api/server/getconfig", allowUsers(http.HandlerFunc(getConfig))). + Methods(http.MethodGet) + r.HandleFunc("/api/server/getserverinfo", Authorize(true, false, "node", http.HandlerFunc(getServerInfo))). + Methods(http.MethodGet) r.HandleFunc("/api/server/status", http.HandlerFunc(getStatus)).Methods(http.MethodGet) - r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet) + r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))). + Methods(http.MethodGet) } -func getUsage(w http.ResponseWriter, r *http.Request) { +func getUsage(w http.ResponseWriter, _ *http.Request) { type usage struct { - Hosts int `json:"hosts"` - Clients int `json:"clients"` - Networks int `json:"networks"` - Users int `json:"users"` - Ingresses int `json:"ingresses"` - Egresses int `json:"egresses"` + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` + Users int `json:"users"` + Ingresses int `json:"ingresses"` + Egresses int `json:"egresses"` + Relays int `json:"relays"` + InternetGateways int `json:"internet_gateways"` } var serverUsage usage hosts, err := logic.GetAllHosts() @@ -51,6 +60,7 @@ func getUsage(w http.ResponseWriter, r *http.Request) { if err == nil { serverUsage.Networks = len(networks) } + // TODO this part bellow can be optimized to get nodes just once ingresses, err := logic.GetAllIngresses() if err == nil { serverUsage.Ingresses = len(ingresses) @@ -59,12 +69,19 @@ func getUsage(w http.ResponseWriter, r *http.Request) { if err == nil { serverUsage.Egresses = len(egresses) } + relays, err := logic.GetRelays() + if err == nil { + serverUsage.Relays = len(relays) + } + gateways, err := logic.GetInternetGateways() + if err == nil { + serverUsage.InternetGateways = len(gateways) + } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(models.SuccessResponse{ Code: http.StatusOK, Response: serverUsage, }) - } // swagger:route GET /api/server/status server getStatus @@ -83,6 +100,7 @@ func getStatus(w http.ResponseWriter, r *http.Request) { DB bool `json:"db_connected"` Broker bool `json:"broker_connected"` LicenseError string `json:"license_error"` + IsPro bool `json:"is_pro"` } licenseErr := "" @@ -94,6 +112,7 @@ func getStatus(w http.ResponseWriter, r *http.Request) { DB: database.IsConnected(), Broker: mq.IsConnected(), LicenseError: licenseErr, + IsPro: servercfg.IsPro, } w.Header().Set("Content-Type", "application/json") @@ -103,12 +122,12 @@ func getStatus(w http.ResponseWriter, r *http.Request) { // allowUsers - allow all authenticated (valid) users - only used by getConfig, may be able to remove during refactor func allowUsers(next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var errorResponse = models.ErrorResponse{ + errorResponse := models.ErrorResponse{ Code: http.StatusUnauthorized, Message: logic.Unauthorized_Msg, } bearerToken := r.Header.Get("Authorization") - var tokenSplit = strings.Split(bearerToken, " ") - var authToken = "" + tokenSplit := strings.Split(bearerToken, " ") + authToken := "" if len(tokenSplit) < 2 { logic.ReturnErrorResponse(w, r, errorResponse) return @@ -142,7 +161,7 @@ func getServerInfo(w http.ResponseWriter, r *http.Request) { // get params json.NewEncoder(w).Encode(servercfg.GetServerInfo()) - //w.WriteHeader(http.StatusOK) + // w.WriteHeader(http.StatusOK) } // swagger:route GET /api/server/getconfig server getConfig @@ -168,5 +187,5 @@ func getConfig(w http.ResponseWriter, r *http.Request) { scfg.IsPro = "yes" } json.NewEncoder(w).Encode(scfg) - //w.WriteHeader(http.StatusOK) + // w.WriteHeader(http.StatusOK) } diff --git a/controllers/user.go b/controllers/user.go index 69f05d677..ff06a67e7 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -12,6 +12,7 @@ import ( "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/mq" "github.com/gravitl/netmaker/servercfg" "golang.org/x/exp/slog" ) @@ -36,9 +37,9 @@ func userHandlers(r *mux.Router) { r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterHostSSO).Methods(http.MethodGet) } -// swagger:route POST /api/users/adm/authenticate user authenticateUser +// swagger:route POST /api/users/adm/authenticate authenticate authenticateUser // -// Node authenticates using its password and retrieves a JWT for authorization. +// User authenticates using its password and retrieves a JWT for authorization. // // Schemes: https // @@ -96,7 +97,6 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { } // Send back the JWT successJSONResponse, jsonError := json.Marshal(successResponse) - if jsonError != nil { logger.Log(0, username, "error marshalling resp: ", err.Error()) @@ -106,6 +106,33 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { logger.Log(2, username, "was authenticated") response.Header().Set("Content-Type", "application/json") response.Write(successJSONResponse) + + go func() { + if servercfg.IsPro && servercfg.GetRacAutoDisable() { + // enable all associeated clients for the user + clients, err := logic.GetAllExtClients() + if err != nil { + slog.Error("error getting clients: ", "error", err) + return + } + for _, client := range clients { + if client.OwnerID == username && !client.Enabled { + slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID)) + if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil { + slog.Error("error disabling ext client in RAC autodisable hook", "error", err) + continue // dont return but try for other clients + } else { + // publish peer update to ingress gateway + if ingressNode, err := logic.GetNodeByID(newClient.IngressGatewayID); err == nil { + if err = mq.PublishPeerUpdate(); err != nil { + slog.Error("error updating ext clients on", "ingress", ingressNode.ID.String(), "err", err.Error()) + } + } + } + } + } + } + }() } // swagger:route GET /api/users/adm/hassuperadmin user hasSuperAdmin @@ -118,7 +145,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { // oauth // // Responses: -// 200: successResponse +// 200: hasAdmin func hasSuperAdmin(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -426,9 +453,8 @@ func updateUser(w http.ResponseWriter, r *http.Request) { } } - if auth.IsOauthUser(user) == nil { - err := fmt.Errorf("cannot update user info for oauth user %s", username) - logger.Log(0, err.Error()) + if auth.IsOauthUser(user) == nil && userchange.Password != "" { + err := fmt.Errorf("cannot update user's password for an oauth user %s", username) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) return } diff --git a/docker/Caddyfile b/docker/Caddyfile index 8fb74138a..4f893bb47 100644 --- a/docker/Caddyfile +++ b/docker/Caddyfile @@ -1,6 +1,5 @@ # Dashboard https://dashboard.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem # Apply basic security headers header { # Enable cross origin access to *.{$NM_DOMAIN} @@ -22,24 +21,20 @@ https://dashboard.{$NM_DOMAIN} { # API https://api.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://netmaker:8081 } # TURN https://turn.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy host.docker.internal:3479 } # TURN API https://turnapi.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://host.docker.internal:8089 } # MQ wss://broker.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy ws://mq:8883 # For EMQX websockets use `reverse_proxy ws://mq:8083` } diff --git a/docker/Caddyfile-pro b/docker/Caddyfile-pro index 2b874debc..a0edf2206 100644 --- a/docker/Caddyfile-pro +++ b/docker/Caddyfile-pro @@ -1,6 +1,5 @@ # Dashboard https://dashboard.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem # Apply basic security headers header { # Enable cross origin access to *.{$NM_DOMAIN} @@ -22,42 +21,35 @@ https://dashboard.{$NM_DOMAIN} { # Netmaker Exporter https://netmaker-exporter.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://netmaker-exporter:8085 } # Prometheus https://prometheus.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://prometheus:9090 } # Grafana https://grafana.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://grafana:3000 } # API https://api.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://netmaker:8081 } # TURN https://turn.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy host.docker.internal:3479 } # TURN API https://turnapi.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy http://host.docker.internal:8089 } # MQ wss://broker.{$NM_DOMAIN} { - tls /root/certs/fullchain.pem /root/certs/privkey.pem reverse_proxy ws://mq:8883 } diff --git a/go.mod b/go.mod index 951c4b1ae..46423f016 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/eclipse/paho.mqtt.golang v1.4.3 - github.com/go-playground/validator/v10 v10.15.1 + github.com/go-playground/validator/v10 v10.15.5 github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/uuid v1.3.1 github.com/gorilla/handlers v1.5.1 @@ -14,12 +14,12 @@ require ( github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/stretchr/testify v1.8.4 - github.com/txn2/txeh v1.5.3 - golang.org/x/crypto v0.12.0 - golang.org/x/net v0.14.0 // indirect - golang.org/x/oauth2 v0.11.0 - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + github.com/txn2/txeh v1.5.5 + golang.org/x/crypto v0.13.0 + golang.org/x/net v0.15.0 // indirect + golang.org/x/oauth2 v0.12.0 + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index f8fed2438..8addbbd63 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM= -github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= +github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -96,33 +96,33 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/txn2/txeh v1.5.3 h1:ZMgc3r+5/AFtE/ayCoICpvxj7xl/CYsZjnIGhozV/Kc= -github.com/txn2/txeh v1.5.3/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4= +github.com/txn2/txeh v1.5.5 h1:UN4e/lCK5HGw/gGAi2GCVrNKg0GTCUWs7gs5riaZlz4= +github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g= github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/oauth2 v0.12.0 h1:smVPGxink+n1ZI5pkQa8y6fZT0RW0MgCO5bFpepy4B4= +golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY= diff --git a/k8s/client/netclient-daemonset.yaml b/k8s/client/netclient-daemonset.yaml index 12c897564..d01612917 100644 --- a/k8s/client/netclient-daemonset.yaml +++ b/k8s/client/netclient-daemonset.yaml @@ -16,7 +16,7 @@ spec: hostNetwork: true containers: - name: netclient - image: gravitl/netclient:v0.21.0 + image: gravitl/netclient:v0.21.1 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/client/netclient.yaml b/k8s/client/netclient.yaml index f329be78a..0152ff960 100644 --- a/k8s/client/netclient.yaml +++ b/k8s/client/netclient.yaml @@ -28,7 +28,7 @@ spec: # - "" containers: - name: netclient - image: gravitl/netclient:v0.21.0 + image: gravitl/netclient:v0.21.1 env: - name: TOKEN value: "TOKEN_VALUE" diff --git a/k8s/server/netmaker-ui.yaml b/k8s/server/netmaker-ui.yaml index 042ea2fdb..bf02d1b01 100644 --- a/k8s/server/netmaker-ui.yaml +++ b/k8s/server/netmaker-ui.yaml @@ -15,7 +15,7 @@ spec: spec: containers: - name: netmaker-ui - image: gravitl/netmaker-ui:v0.21.0 + image: gravitl/netmaker-ui:v0.21.1 ports: - containerPort: 443 env: diff --git a/logic/auth.go b/logic/auth.go index 89eb27e51..10f5d51a4 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -112,6 +112,7 @@ func CreateSuperAdmin(u *models.User) error { return errors.New("superadmin user already exists") } u.IsSuperAdmin = true + u.IsAdmin = false return CreateUser(u) } @@ -141,6 +142,11 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { // Create a new JWT for the node tokenString, _ := CreateUserJWT(authRequest.UserName, result.IsSuperAdmin, result.IsAdmin) + + // update last login time + result.LastLoginTime = time.Now() + UpsertUser(result) + return tokenString, nil } diff --git a/logic/dns.go b/logic/dns.go index d1b75c3da..af244c9dd 100644 --- a/logic/dns.go +++ b/logic/dns.go @@ -32,7 +32,7 @@ func SetDNS() error { return err } for _, entry := range dns { - hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network) + hostfile.AddHost(entry.Address, entry.Name) } } if corefilestring == "" { diff --git a/logic/extpeers.go b/logic/extpeers.go index 7e3fb2fbe..7d301c5a0 100644 --- a/logic/extpeers.go +++ b/logic/extpeers.go @@ -9,6 +9,7 @@ import ( "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/models" + "golang.org/x/exp/slog" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) @@ -225,7 +226,7 @@ func UpdateExtClient(old *models.ExtClient, update *models.CustomExtClient) mode if update.PublicKey != "" && old.PublicKey != update.PublicKey { new.PublicKey = update.PublicKey } - if update.DNS != "" && update.DNS != old.DNS { + if update.DNS != old.DNS { new.DNS = update.DNS } if update.Enabled != old.Enabled { @@ -276,3 +277,29 @@ func GetAllExtClients() ([]models.ExtClient, error) { return clients, nil } + +// ToggleExtClientConnectivity - enables or disables an ext client +func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.ExtClient, error) { + update := models.CustomExtClient{ + Enabled: enable, + ClientID: client.ClientID, + PublicKey: client.PublicKey, + DNS: client.DNS, + ExtraAllowedIPs: client.ExtraAllowedIPs, + DeniedACLs: client.DeniedACLs, + RemoteAccessClientID: client.RemoteAccessClientID, + } + + // update in DB + newClient := UpdateExtClient(client, &update) + if err := DeleteExtClient(client.Network, client.ClientID); err != nil { + slog.Error("failed to delete ext client during update", "id", client.ClientID, "network", client.Network, "error", err) + return newClient, err + } + if err := SaveExtClient(&newClient); err != nil { + slog.Error("failed to save updated ext client during update", "id", newClient.ClientID, "network", newClient.Network, "error", err) + return newClient, err + } + + return newClient, nil +} diff --git a/logic/gateway.go b/logic/gateway.go index 2f7234698..342dacb30 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -11,7 +11,27 @@ import ( "github.com/gravitl/netmaker/servercfg" ) -// GetAllIngresses - gets all the hosts that are ingresses +// GetInternetGateways - gets all the nodes that are internet gateways +func GetInternetGateways() ([]models.Node, error) { + nodes, err := GetAllNodes() + if err != nil { + return nil, err + } + igs := make([]models.Node, 0) + for _, node := range nodes { + if !node.IsEgressGateway { + continue + } + for _, ran := range node.EgressGatewayRanges { + if ran == "0.0.0.0/0" { + igs = append(igs, node) + } + } + } + return igs, nil +} + +// GetAllIngresses - gets all the nodes that are ingresses func GetAllIngresses() ([]models.Node, error) { nodes, err := GetAllNodes() if err != nil { @@ -26,7 +46,7 @@ func GetAllIngresses() ([]models.Node, error) { return ingresses, nil } -// GetAllEgresses - gets all the hosts that are egresses +// GetAllEgresses - gets all the nodes that are egresses func GetAllEgresses() ([]models.Node, error) { nodes, err := GetAllNodes() if err != nil { diff --git a/logic/hosts.go b/logic/hosts.go index e17c6f1e7..2137706ff 100644 --- a/logic/hosts.go +++ b/logic/hosts.go @@ -13,11 +13,12 @@ import ( "github.com/devilcove/httpclient" "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" - "golang.org/x/crypto/bcrypt" ) var ( @@ -66,6 +67,7 @@ func deleteHostFromCache(hostID string) { delete(hostsCacheMap, hostID) hostCacheMutex.Unlock() } + func loadHostsIntoCache(hMap map[string]models.Host) { hostCacheMutex.Lock() hostsCacheMap = hMap @@ -79,7 +81,6 @@ const ( // GetAllHosts - returns all hosts in flat list or error func GetAllHosts() ([]models.Host, error) { - currHosts := getHostsFromCache() if len(currHosts) != 0 { return currHosts, nil @@ -139,7 +140,6 @@ func GetHostsMap() (map[string]models.Host, error) { // GetHost - gets a host from db given id func GetHost(hostid string) (*models.Host, error) { - if host, ok := getHostFromCache(hostid); ok { return &host, nil } @@ -217,11 +217,13 @@ func UpdateHost(newHost, currentHost *models.Host) { newHost.ListenPort = currentHost.ListenPort } + if newHost.PersistentKeepalive == 0 { + newHost.PersistentKeepalive = currentHost.PersistentKeepalive + } } // UpdateHostFromClient - used for updating host on server with update recieved from client func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) { - if newHost.PublicKey != currHost.PublicKey { currHost.PublicKey = newHost.PublicKey sendPeerUpdate = true @@ -230,7 +232,8 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool) currHost.ListenPort = newHost.ListenPort sendPeerUpdate = true } - if newHost.WgPublicListenPort != 0 && currHost.WgPublicListenPort != newHost.WgPublicListenPort { + if newHost.WgPublicListenPort != 0 && + currHost.WgPublicListenPort != newHost.WgPublicListenPort { currHost.WgPublicListenPort = newHost.WgPublicListenPort sendPeerUpdate = true } @@ -488,7 +491,7 @@ func CheckHostPorts(h *models.Host) { } for _, host := range hosts { if host.ID.String() == h.ID.String() { - //skip self + // skip self continue } if !host.EndpointIP.Equal(h.EndpointIP) { @@ -503,7 +506,6 @@ func CheckHostPorts(h *models.Host) { h.ListenPort = minPort } } - } // HostExists - checks if given host already exists diff --git a/logic/jwts.go b/logic/jwts.go index 49e15d456..105546925 100644 --- a/logic/jwts.go +++ b/logic/jwts.go @@ -54,7 +54,7 @@ func CreateJWT(uuid string, macAddress string, network string) (response string, // CreateUserJWT - creates a user jwt token func CreateUserJWT(username string, issuperadmin, isadmin bool) (response string, err error) { - expirationTime := time.Now().Add(60 * 12 * time.Minute) + expirationTime := time.Now().Add(servercfg.GetServerConfig().JwtValidityDuration) claims := &models.UserClaims{ UserName: username, IsSuperAdmin: issuperadmin, diff --git a/logic/nodes.go b/logic/nodes.go index 1178ca61e..c9cf8da01 100644 --- a/logic/nodes.go +++ b/logic/nodes.go @@ -350,9 +350,6 @@ func SetNodeDefaults(node *models.Node) { node.DefaultACL = parentNetwork.DefaultACL } - if node.PersistentKeepalive == 0 { - node.PersistentKeepalive = time.Second * time.Duration(parentNetwork.DefaultKeepalive) - } node.SetLastModified() node.SetLastCheckIn() node.SetDefaultConnected() @@ -425,12 +422,14 @@ func GetAllNodesAPI(nodes []models.Node) []models.ApiNode { // DeleteExpiredNodes - goroutine which deletes nodes which are expired func DeleteExpiredNodes(ctx context.Context, peerUpdate chan *models.Node) { + // Delete Expired Nodes Every Hour + ticker := time.NewTicker(time.Hour) for { select { case <-ctx.Done(): + ticker.Stop() return - case <-time.After(time.Hour): - // Delete Expired Nodes Every Hour + case <-ticker.C: allnodes, err := GetAllNodes() if err != nil { slog.Error("failed to retrieve all nodes", "error", err.Error()) diff --git a/logic/peers.go b/logic/peers.go index 33a19c99d..ada95ac91 100644 --- a/logic/peers.go +++ b/logic/peers.go @@ -64,7 +64,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N } relayPeer := wgtypes.PeerConfig{ PublicKey: relayHost.PublicKey, - PersistentKeepaliveInterval: &relayNode.PersistentKeepalive, + PersistentKeepaliveInterval: &relayHost.PersistentKeepalive, ReplaceAllowedIPs: true, AllowedIPs: GetAllowedIPs(&node, &relayNode, nil), } @@ -111,7 +111,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N peer := peer if peer.ID.String() == node.ID.String() { logger.Log(2, "peer update, skipping self") - //skip yourself + // skip yourself continue } @@ -122,7 +122,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N } peerConfig := wgtypes.PeerConfig{ PublicKey: peerHost.PublicKey, - PersistentKeepaliveInterval: &peer.PersistentKeepalive, + PersistentKeepaliveInterval: &peerHost.PersistentKeepalive, ReplaceAllowedIPs: true, } if peer.IsEgressGateway { @@ -173,14 +173,13 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection } - peerPort := GetPeerListenPort(peerHost) var nodePeer wgtypes.PeerConfig if _, ok := peerIndexMap[peerHost.PublicKey.String()]; !ok { hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, peerConfig) peerIndexMap[peerHost.PublicKey.String()] = len(hostPeerUpdate.Peers) - 1 hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{ Interfaces: peerHost.Interfaces, - ListenPort: peerPort, + ListenPort: peerHost.ListenPort, IsStatic: peerHost.IsStatic, } nodePeer = peerConfig @@ -191,7 +190,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]].Remove = false hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{ Interfaces: peerHost.Interfaces, - ListenPort: peerPort, + ListenPort: peerHost.ListenPort, IsStatic: peerHost.IsStatic, } nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]] @@ -391,7 +390,7 @@ func GetEgressIPs(peer *models.Node) []net.IPNet { logger.Log(0, "error retrieving host for peer", peer.ID.String(), err.Error()) } - //check for internet gateway + // check for internet gateway internetGateway := false if slices.Contains(peer.EgressGatewayRanges, "0.0.0.0/0") || slices.Contains(peer.EgressGatewayRanges, "::/0") { internetGateway = true @@ -440,7 +439,7 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet { } // handle egress gateway peers if peer.IsEgressGateway { - //hasGateway = true + // hasGateway = true egressIPs := GetEgressIPs(peer) allowedips = append(allowedips, egressIPs...) } diff --git a/logic/relay.go b/logic/relay.go index 394f3cf28..8c89b3d51 100644 --- a/logic/relay.go +++ b/logic/relay.go @@ -5,6 +5,10 @@ import ( "net" ) +var GetRelays = func() ([]models.Node, error) { + return []models.Node{}, nil +} + var RelayedAllowedIPs = func(peer, node *models.Node) []net.IPNet { return []net.IPNet{} } diff --git a/logic/timer.go b/logic/timer.go index 7a21a3785..2d0fbb6e8 100644 --- a/logic/timer.go +++ b/logic/timer.go @@ -17,7 +17,7 @@ import ( const timer_hours_between_runs = 24 // HookManagerCh - channel to add any new hooks -var HookManagerCh = make(chan models.HookDetails, 2) +var HookManagerCh = make(chan models.HookDetails, 3) // == Public == diff --git a/logic/wireguard.go b/logic/wireguard.go index 7ef6cbabf..0828ff6ff 100644 --- a/logic/wireguard.go +++ b/logic/wireguard.go @@ -12,7 +12,6 @@ func IfaceDelta(currentNode *models.Node, newNode *models.Node) bool { newNode.IsEgressGateway != currentNode.IsEgressGateway || newNode.IsIngressGateway != currentNode.IsIngressGateway || newNode.IsRelay != currentNode.IsRelay || - newNode.PersistentKeepalive != currentNode.PersistentKeepalive || newNode.DNSOn != currentNode.DNSOn || newNode.Connected != currentNode.Connected { return true diff --git a/logic/zombie.go b/logic/zombie.go index c7e858a88..d191aaa3d 100644 --- a/logic/zombie.go +++ b/logic/zombie.go @@ -77,16 +77,21 @@ func checkForZombieHosts(h *models.Host) { func ManageZombies(ctx context.Context, peerUpdate chan *models.Node) { logger.Log(2, "Zombie management started") InitializeZombies() + + // Zombie Nodes Cleanup Four Times a Day + ticker := time.NewTicker(time.Hour * ZOMBIE_TIMEOUT) + for { select { case <-ctx.Done(): + ticker.Stop() close(peerUpdate) return case id := <-newZombie: zombies = append(zombies, id) case id := <-newHostZombie: hostZombies = append(hostZombies, id) - case <-time.After(time.Hour * ZOMBIE_TIMEOUT): // run this check 4 times a day + case <-ticker.C: // run this check 4 times a day logger.Log(3, "checking for zombie nodes") if len(zombies) > 0 { for i := len(zombies) - 1; i >= 0; i-- { diff --git a/main.go b/main.go index 1861ee212..c93fcb495 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,7 @@ import ( "golang.org/x/exp/slog" ) -var version = "v0.21.0" +var version = "v0.21.1" // Start DB Connection and start API Request Handler func main() { diff --git a/migrate/migrate.go b/migrate/migrate.go index 22b70251d..371ffe30c 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -3,17 +3,19 @@ package migrate import ( "encoding/json" + "golang.org/x/exp/slog" + "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" - "golang.org/x/exp/slog" ) // Run - runs all migrations func Run() { updateEnrollmentKeys() assignSuperAdmin() + updateHosts() } func assignSuperAdmin() { @@ -37,7 +39,13 @@ func assignSuperAdmin() { user.IsAdmin = false err = logic.UpsertUser(*user) if err != nil { - slog.Error("error updating user to superadmin", "user", user.UserName, "error", err.Error()) + slog.Error( + "error updating user to superadmin", + "user", + user.UserName, + "error", + err.Error(), + ) continue } else { createdSuperAdmin = true @@ -49,7 +57,6 @@ func assignSuperAdmin() { if !createdSuperAdmin { slog.Error("failed to create superadmin!!") } - } func updateEnrollmentKeys() { @@ -87,3 +94,24 @@ func updateEnrollmentKeys() { } } + +func updateHosts() { + rows, err := database.FetchRecords(database.HOSTS_TABLE_NAME) + if err != nil { + logger.Log(0, "failed to fetch database records for hosts") + } + for _, row := range rows { + var host models.Host + if err := json.Unmarshal([]byte(row), &host); err != nil { + logger.Log(0, "failed to unmarshal database row to host", "row", row) + continue + } + if host.PersistentKeepalive == 0 { + host.PersistentKeepalive = models.DefaultPersistentKeepAlive + if err := logic.UpsertHost(&host); err != nil { + logger.Log(0, "failed to upsert host", host.ID.String()) + continue + } + } + } +} diff --git a/models/api_host.go b/models/api_host.go index 4558a984b..7dd9fdf9e 100644 --- a/models/api_host.go +++ b/models/api_host.go @@ -3,33 +3,35 @@ package models import ( "net" "strings" + "time" ) // ApiHost - the host struct for API usage type ApiHost struct { - ID string `json:"id"` - Verbosity int `json:"verbosity"` - FirewallInUse string `json:"firewallinuse"` - Version string `json:"version"` - Name string `json:"name"` - OS string `json:"os"` - Debug bool `json:"debug"` - IsStatic bool `json:"isstatic"` - ListenPort int `json:"listenport"` - WgPublicListenPort int `json:"wg_public_listen_port" yaml:"wg_public_listen_port"` - MTU int `json:"mtu" yaml:"mtu"` - Interfaces []Iface `json:"interfaces" yaml:"interfaces"` - DefaultInterface string `json:"defaultinterface" yaml:"defautlinterface"` - EndpointIP string `json:"endpointip" yaml:"endpointip"` - PublicKey string `json:"publickey"` - MacAddress string `json:"macaddress"` - Nodes []string `json:"nodes"` - IsDefault bool `json:"isdefault" yaml:"isdefault"` - IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` - RelayedBy string `json:"relayed_by" bson:"relayed_by" yaml:"relayed_by"` - IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` - RelayedHosts []string `json:"relay_hosts" bson:"relay_hosts" yaml:"relay_hosts"` - NatType string `json:"nat_type" yaml:"nat_type"` + ID string `json:"id"` + Verbosity int `json:"verbosity"` + FirewallInUse string `json:"firewallinuse"` + Version string `json:"version"` + Name string `json:"name"` + OS string `json:"os"` + Debug bool `json:"debug"` + IsStatic bool `json:"isstatic"` + ListenPort int `json:"listenport"` + WgPublicListenPort int `json:"wg_public_listen_port" yaml:"wg_public_listen_port"` + MTU int `json:"mtu" yaml:"mtu"` + Interfaces []Iface `json:"interfaces" yaml:"interfaces"` + DefaultInterface string `json:"defaultinterface" yaml:"defautlinterface"` + EndpointIP string `json:"endpointip" yaml:"endpointip"` + PublicKey string `json:"publickey"` + MacAddress string `json:"macaddress"` + Nodes []string `json:"nodes"` + IsDefault bool `json:"isdefault" yaml:"isdefault"` + IsRelayed bool `json:"isrelayed" yaml:"isrelayed" bson:"isrelayed"` + RelayedBy string `json:"relayed_by" yaml:"relayed_by" bson:"relayed_by"` + IsRelay bool `json:"isrelay" yaml:"isrelay" bson:"isrelay"` + RelayedHosts []string `json:"relay_hosts" yaml:"relay_hosts" bson:"relay_hosts"` + NatType string `json:"nat_type" yaml:"nat_type"` + PersistentKeepalive int `json:"persistentkeepalive" yaml:"persistentkeepalive"` } // Host.ConvertNMHostToAPI - converts a Netmaker host to an API editable host @@ -57,6 +59,7 @@ func (h *Host) ConvertNMHostToAPI() *ApiHost { a.Version = h.Version a.IsDefault = h.IsDefault a.NatType = h.NatType + a.PersistentKeepalive = int(h.PersistentKeepalive.Seconds()) return &a } @@ -94,6 +97,6 @@ func (a *ApiHost) ConvertAPIHostToNMHost(currentHost *Host) *Host { h.IsDefault = a.IsDefault h.NatType = currentHost.NatType h.TurnEndpoint = currentHost.TurnEndpoint - + h.PersistentKeepalive = time.Duration(a.PersistentKeepalive) * time.Second return &h } diff --git a/models/api_node.go b/models/api_node.go index 843b0bf23..5dea54ae3 100644 --- a/models/api_node.go +++ b/models/api_node.go @@ -15,7 +15,6 @@ type ApiNode struct { Address6 string `json:"address6" validate:"omitempty,ipv6"` LocalAddress string `json:"localaddress" validate:"omitempty,ipv4"` AllowedIPs []string `json:"allowedips"` - PersistentKeepalive int32 `json:"persistentkeepalive"` LastModified int64 `json:"lastmodified"` ExpirationDateTime int64 `json:"expdatetime"` LastCheckIn int64 `json:"lastcheckin"` @@ -68,7 +67,6 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node { convertedNode.IngressDNS = a.IngressDns convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled - convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive) convertedNode.RelayedNodes = a.RelayedNodes convertedNode.DefaultACL = a.DefaultACL convertedNode.OwnerID = currentNode.OwnerID @@ -127,7 +125,6 @@ func (nm *Node) ConvertToAPINode() *ApiNode { if isEmptyAddr(apiNode.LocalAddress) { apiNode.LocalAddress = "" } - apiNode.PersistentKeepalive = int32(nm.PersistentKeepalive.Seconds()) apiNode.LastModified = nm.LastModified.Unix() apiNode.LastCheckIn = nm.LastCheckIn.Unix() apiNode.LastPeerUpdate = nm.LastPeerUpdate.Unix() diff --git a/models/host.go b/models/host.go index 2ab90e16d..4a2715b50 100644 --- a/models/host.go +++ b/models/host.go @@ -3,6 +3,7 @@ package models import ( "net" "net/netip" + "time" "github.com/google/uuid" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" @@ -33,38 +34,42 @@ var NAT_Types = struct { } // WIREGUARD_INTERFACE name of wireguard interface -const WIREGUARD_INTERFACE = "netmaker" +const ( + WIREGUARD_INTERFACE = "netmaker" + DefaultPersistentKeepAlive = 20 * time.Second +) // Host - represents a host on the network type Host struct { - ID uuid.UUID `json:"id" yaml:"id"` - Verbosity int `json:"verbosity" yaml:"verbosity"` - FirewallInUse string `json:"firewallinuse" yaml:"firewallinuse"` - Version string `json:"version" yaml:"version"` - IPForwarding bool `json:"ipforwarding" yaml:"ipforwarding"` - DaemonInstalled bool `json:"daemoninstalled" yaml:"daemoninstalled"` - AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"` - HostPass string `json:"hostpass" yaml:"hostpass"` - Name string `json:"name" yaml:"name"` - OS string `json:"os" yaml:"os"` - Interface string `json:"interface" yaml:"interface"` - Debug bool `json:"debug" yaml:"debug"` - ListenPort int `json:"listenport" yaml:"listenport"` - WgPublicListenPort int `json:"wg_public_listen_port" yaml:"wg_public_listen_port"` - MTU int `json:"mtu" yaml:"mtu"` - PublicKey wgtypes.Key `json:"publickey" yaml:"publickey"` - MacAddress net.HardwareAddr `json:"macaddress" yaml:"macaddress"` - TrafficKeyPublic []byte `json:"traffickeypublic" yaml:"traffickeypublic"` - Nodes []string `json:"nodes" yaml:"nodes"` - Interfaces []Iface `json:"interfaces" yaml:"interfaces"` - DefaultInterface string `json:"defaultinterface" yaml:"defaultinterface"` - EndpointIP net.IP `json:"endpointip" yaml:"endpointip"` - IsDocker bool `json:"isdocker" yaml:"isdocker"` - IsK8S bool `json:"isk8s" yaml:"isk8s"` - IsStatic bool `json:"isstatic" yaml:"isstatic"` - IsDefault bool `json:"isdefault" yaml:"isdefault"` - NatType string `json:"nat_type,omitempty" yaml:"nat_type,omitempty"` - TurnEndpoint *netip.AddrPort `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"` + ID uuid.UUID `json:"id" yaml:"id"` + Verbosity int `json:"verbosity" yaml:"verbosity"` + FirewallInUse string `json:"firewallinuse" yaml:"firewallinuse"` + Version string `json:"version" yaml:"version"` + IPForwarding bool `json:"ipforwarding" yaml:"ipforwarding"` + DaemonInstalled bool `json:"daemoninstalled" yaml:"daemoninstalled"` + AutoUpdate bool `json:"autoupdate" yaml:"autoupdate"` + HostPass string `json:"hostpass" yaml:"hostpass"` + Name string `json:"name" yaml:"name"` + OS string `json:"os" yaml:"os"` + Interface string `json:"interface" yaml:"interface"` + Debug bool `json:"debug" yaml:"debug"` + ListenPort int `json:"listenport" yaml:"listenport"` + WgPublicListenPort int `json:"wg_public_listen_port" yaml:"wg_public_listen_port"` + MTU int `json:"mtu" yaml:"mtu"` + PublicKey wgtypes.Key `json:"publickey" yaml:"publickey"` + MacAddress net.HardwareAddr `json:"macaddress" yaml:"macaddress"` + TrafficKeyPublic []byte `json:"traffickeypublic" yaml:"traffickeypublic"` + Nodes []string `json:"nodes" yaml:"nodes"` + Interfaces []Iface `json:"interfaces" yaml:"interfaces"` + DefaultInterface string `json:"defaultinterface" yaml:"defaultinterface"` + EndpointIP net.IP `json:"endpointip" yaml:"endpointip"` + IsDocker bool `json:"isdocker" yaml:"isdocker"` + IsK8S bool `json:"isk8s" yaml:"isk8s"` + IsStatic bool `json:"isstatic" yaml:"isstatic"` + IsDefault bool `json:"isdefault" yaml:"isdefault"` + NatType string `json:"nat_type,omitempty" yaml:"nat_type,omitempty"` + TurnEndpoint *netip.AddrPort `json:"turn_endpoint,omitempty" yaml:"turn_endpoint,omitempty"` + PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"` } // FormatBool converts a boolean to a [yes|no] string @@ -89,26 +94,28 @@ func ParseBool(s string) bool { type HostMqAction string const ( + // Upgrade - const to request host to update it's client + Upgrade HostMqAction = "UPGRADE" // SignalHost - const for host signal action - SignalHost = "SIGNAL_HOST" + SignalHost HostMqAction = "SIGNAL_HOST" // UpdateHost - constant for host update action - UpdateHost = "UPDATE_HOST" + UpdateHost HostMqAction = "UPDATE_HOST" // DeleteHost - constant for host delete action - DeleteHost = "DELETE_HOST" + DeleteHost HostMqAction = "DELETE_HOST" // JoinHostToNetwork - constant for host network join action - JoinHostToNetwork = "JOIN_HOST_TO_NETWORK" + JoinHostToNetwork HostMqAction = "JOIN_HOST_TO_NETWORK" // Acknowledgement - ACK response for hosts - Acknowledgement = "ACK" + Acknowledgement HostMqAction = "ACK" // RequestAck - request an ACK - RequestAck = "REQ_ACK" + RequestAck HostMqAction = "REQ_ACK" // CheckIn - update last check in times and public address and interfaces - CheckIn = "CHECK_IN" - // REGISTER_WITH_TURN - registers host with turn server if configured - RegisterWithTurn = "REGISTER_WITH_TURN" + CheckIn HostMqAction = "CHECK_IN" + // RegisterWithTurn - registers host with turn server if configured + RegisterWithTurn HostMqAction = "REGISTER_WITH_TURN" // UpdateKeys - update wireguard private/public keys - UpdateKeys = "UPDATE_KEYS" + UpdateKeys HostMqAction = "UPDATE_KEYS" // RequestPull - request a pull from a host - RequestPull = "REQ_PULL" + RequestPull HostMqAction = "REQ_PULL" ) // SignalAction - turn peer signal action diff --git a/models/node.go b/models/node.go index b011321ef..777c6aaf0 100644 --- a/models/node.go +++ b/models/node.go @@ -54,28 +54,27 @@ type Iface struct { // CommonNode - represents a commonn node data elements shared by netmaker and netclient type CommonNode struct { - ID uuid.UUID `json:"id" yaml:"id"` - HostID uuid.UUID `json:"hostid" yaml:"hostid"` - Network string `json:"network" yaml:"network"` - NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` - NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` - InternetGateway *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"` - Server string `json:"server" yaml:"server"` - Connected bool `json:"connected" yaml:"connected"` - Address net.IPNet `json:"address" yaml:"address"` - Address6 net.IPNet `json:"address6" yaml:"address6"` - Action string `json:"action" yaml:"action"` - LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` - IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` - EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` - IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` - IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` - RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` - IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` - RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` - IngressDNS string `json:"ingressdns" yaml:"ingressdns"` - DNSOn bool `json:"dnson" yaml:"dnson"` - PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"` + ID uuid.UUID `json:"id" yaml:"id"` + HostID uuid.UUID `json:"hostid" yaml:"hostid"` + Network string `json:"network" yaml:"network"` + NetworkRange net.IPNet `json:"networkrange" yaml:"networkrange"` + NetworkRange6 net.IPNet `json:"networkrange6" yaml:"networkrange6"` + InternetGateway *net.UDPAddr `json:"internetgateway" yaml:"internetgateway"` + Server string `json:"server" yaml:"server"` + Connected bool `json:"connected" yaml:"connected"` + Address net.IPNet `json:"address" yaml:"address"` + Address6 net.IPNet `json:"address6" yaml:"address6"` + Action string `json:"action" yaml:"action"` + LocalAddress net.IPNet `json:"localaddress" yaml:"localaddress"` + IsEgressGateway bool `json:"isegressgateway" yaml:"isegressgateway"` + EgressGatewayRanges []string `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"` + IsIngressGateway bool `json:"isingressgateway" yaml:"isingressgateway"` + IsRelayed bool `json:"isrelayed" bson:"isrelayed" yaml:"isrelayed"` + RelayedBy string `json:"relayedby" bson:"relayedby" yaml:"relayedby"` + IsRelay bool `json:"isrelay" bson:"isrelay" yaml:"isrelay"` + RelayedNodes []string `json:"relaynodes" yaml:"relayedNodes"` + IngressDNS string `json:"ingressdns" yaml:"ingressdns"` + DNSOn bool `json:"dnson" yaml:"dnson"` } // Node - a model of a network node @@ -369,9 +368,6 @@ func (newNode *Node) Fill(currentNode *Node, isPro bool) { // TODO add new field if newNode.Address6.String() == "" { newNode.Address6 = currentNode.Address6 } - if newNode.PersistentKeepalive < 0 { - newNode.PersistentKeepalive = currentNode.PersistentKeepalive - } if newNode.LastModified != currentNode.LastModified { newNode.LastModified = currentNode.LastModified } diff --git a/models/structs.go b/models/structs.go index eed8f4478..c67d5eeea 100644 --- a/models/structs.go +++ b/models/structs.go @@ -24,19 +24,21 @@ type AuthParams struct { // User struct - struct for Users type User struct { - UserName string `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"` - Password string `json:"password" bson:"password" validate:"required,min=5"` - IsAdmin bool `json:"isadmin" bson:"isadmin"` - IsSuperAdmin bool `json:"issuperadmin"` - RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` + UserName string `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"` + Password string `json:"password" bson:"password" validate:"required,min=5"` + IsAdmin bool `json:"isadmin" bson:"isadmin"` + IsSuperAdmin bool `json:"issuperadmin"` + RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` + LastLoginTime time.Time `json:"last_login_time"` } // ReturnUser - return user struct type ReturnUser struct { - UserName string `json:"username"` - IsAdmin bool `json:"isadmin"` - IsSuperAdmin bool `json:"issuperadmin"` - RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` + UserName string `json:"username"` + IsAdmin bool `json:"isadmin"` + IsSuperAdmin bool `json:"issuperadmin"` + RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` + LastLoginTime time.Time `json:"last_login_time"` } // UserAuthParams - user auth params struct @@ -248,23 +250,22 @@ type NodeJoinResponse struct { // ServerConfig - struct for dealing with the server information for a netclient type ServerConfig struct { - CoreDNSAddr string `yaml:"corednsaddr"` - API string `yaml:"api"` - APIPort string `yaml:"apiport"` - DNSMode string `yaml:"dnsmode"` - Version string `yaml:"version"` - MQPort string `yaml:"mqport"` - MQUserName string `yaml:"mq_username"` - MQPassword string `yaml:"mq_password"` - Server string `yaml:"server"` - Broker string `yaml:"broker"` - IsPro bool `yaml:"isee" json:"Is_EE"` - StunPort int `yaml:"stun_port"` - StunList []StunServer `yaml:"stun_list"` - TrafficKey []byte `yaml:"traffickey"` - TurnDomain string `yaml:"turn_domain"` - TurnPort int `yaml:"turn_port"` - UseTurn bool `yaml:"use_turn"` + CoreDNSAddr string `yaml:"corednsaddr"` + API string `yaml:"api"` + APIPort string `yaml:"apiport"` + DNSMode string `yaml:"dnsmode"` + Version string `yaml:"version"` + MQPort string `yaml:"mqport"` + MQUserName string `yaml:"mq_username"` + MQPassword string `yaml:"mq_password"` + Server string `yaml:"server"` + Broker string `yaml:"broker"` + IsPro bool `yaml:"isee" json:"Is_EE"` + StunPort int `yaml:"stun_port"` + TrafficKey []byte `yaml:"traffickey"` + TurnDomain string `yaml:"turn_domain"` + TurnPort int `yaml:"turn_port"` + UseTurn bool `yaml:"use_turn"` } // User.NameInCharset - returns if name is in charset below or not @@ -290,12 +291,6 @@ type JoinData struct { Key string `json:"key" yaml:"key"` } -// StunServer - struct to hold data required for using stun server -type StunServer struct { - Domain string `json:"domain" yaml:"domain"` - Port int `json:"port" yaml:"port"` -} - // HookDetails - struct to hold hook info type HookDetails struct { Hook func() error diff --git a/mq/handlers.go b/mq/handlers.go index e2acc7302..84ffc81e4 100644 --- a/mq/handlers.go +++ b/mq/handlers.go @@ -3,10 +3,10 @@ package mq import ( "encoding/json" "fmt" + mqtt "github.com/eclipse/paho.mqtt.golang" "github.com/google/uuid" "github.com/gravitl/netmaker/database" - "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic/hostactions" "github.com/gravitl/netmaker/models" @@ -20,15 +20,6 @@ import ( var UpdateMetrics = func(client mqtt.Client, msg mqtt.Message) { } -func RunUpdates(node *models.Node, ifaceDelta bool) { - go func() { // don't block http response - // publish node update if not server - if err := NodeUpdate(node); err != nil { - logger.Log(1, "error publishing node update to node", node.ID.String(), err.Error()) - } - }() -} - // DefaultHandler default message queue handler -- NOT USED func DefaultHandler(client mqtt.Client, msg mqtt.Message) { slog.Info("mqtt default handler", "topic", msg.Topic(), "message", msg.Payload()) diff --git a/mq/publishers.go b/mq/publishers.go index 50f1040fd..83de5e63f 100644 --- a/mq/publishers.go +++ b/mq/publishers.go @@ -315,7 +315,7 @@ func PublishDeleteExtClientDNS(client *models.ExtClient) error { func PublishCustomDNS(entry *models.DNSEntry) error { dns := models.DNSUpdate{ Action: models.DNSInsert, - Name: entry.Name + "." + entry.Network, + Name: entry.Name, //entry.Address6 is never used Address: entry.Address, } diff --git a/pro/controllers/users.go b/pro/controllers/users.go index 8c10800aa..0cbcad5d7 100644 --- a/pro/controllers/users.go +++ b/pro/controllers/users.go @@ -78,7 +78,7 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) { // swagger:route DELETE /api/users/{username}/remote_access_gw user removeUserFromRemoteAccessGW // -// Attach User to a remote access gateway. +// Delete User from a remote access gateway. // // Schemes: https // diff --git a/pro/initialize.go b/pro/initialize.go index 4b8abee36..46076ef26 100644 --- a/pro/initialize.go +++ b/pro/initialize.go @@ -38,6 +38,9 @@ func InitPro() { logic.SetFreeTierForTelemetry(false) // == End License Handling == AddLicenseHooks() + if servercfg.GetServerConfig().RacAutoDisable { + AddRacHooks() + } resetFailover() }) logic.EnterpriseFailoverFunc = proLogic.SetFailover @@ -52,6 +55,7 @@ func InitPro() { logic.GetMetrics = proLogic.GetMetrics logic.UpdateMetrics = proLogic.UpdateMetrics logic.DeleteMetrics = proLogic.DeleteMetrics + logic.GetRelays = proLogic.GetRelays logic.GetAllowedIpsForRelayed = proLogic.GetAllowedIpsForRelayed logic.RelayedAllowedIPs = proLogic.RelayedAllowedIPs logic.UpdateRelayed = proLogic.UpdateRelayed diff --git a/pro/license.go b/pro/license.go index 4e25026ab..c76505348 100644 --- a/pro/license.go +++ b/pro/license.go @@ -9,17 +9,18 @@ import ( "encoding/json" "errors" "fmt" - "golang.org/x/exp/slog" "io" "net/http" "time" + "golang.org/x/crypto/nacl/box" + "golang.org/x/exp/slog" + "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/netclient/ncutils" "github.com/gravitl/netmaker/servercfg" - "golang.org/x/crypto/nacl/box" ) const ( @@ -28,7 +29,7 @@ const ( type apiServerConf struct { PrivateKey []byte `json:"private_key" binding:"required"` - PublicKey []byte `json:"public_key" binding:"required"` + PublicKey []byte `json:"public_key" binding:"required"` } // AddLicenseHooks - adds the validation and cache clear hooks @@ -51,8 +52,8 @@ func ValidateLicense() (err error) { defer func() { if err != nil { err = fmt.Errorf("%w: %s", errValidation, err.Error()) - servercfg.ErrLicenseValidation = err } + servercfg.ErrLicenseValidation = err }() licenseKeyValue := servercfg.GetLicenseKey() @@ -112,7 +113,11 @@ func ValidateLicense() (err error) { return err } - respData, err := ncutils.BoxDecrypt(base64decode(licenseResponse.EncryptedLicense), apiPublicKey, tempPrivKey) + respData, err := ncutils.BoxDecrypt( + base64decode(licenseResponse.EncryptedLicense), + apiPublicKey, + tempPrivKey, + ) if err != nil { err = fmt.Errorf("failed to decrypt license: %w", err) return err @@ -132,7 +137,7 @@ func ValidateLicense() (err error) { // as well as secure communication with API // if none present, it generates a new pair func FetchApiServerKeys() (pub *[32]byte, priv *[32]byte, err error) { - var returnData = apiServerConf{} + returnData := apiServerConf{} currentData, err := database.FetchRecord(database.SERVERCONF_TABLE_NAME, db_license_key) if err != nil && !database.IsEmptyRecord(err) { return nil, nil, err @@ -181,7 +186,6 @@ func getLicensePublicKey(licensePubKeyEncoded string) (*[32]byte, error) { } func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, error) { - publicKeyBytes, err := ncutils.ConvertKeyToBytes(publicKey) if err != nil { return nil, err @@ -198,7 +202,11 @@ func validateLicenseKey(encryptedData []byte, publicKey *[32]byte) ([]byte, erro return nil, err } - req, err := http.NewRequest(http.MethodPost, getAccountsHost()+"/api/v1/license/validate", bytes.NewReader(requestBody)) + req, err := http.NewRequest( + http.MethodPost, + getAccountsHost()+"/api/v1/license/validate", + bytes.NewReader(requestBody), + ) if err != nil { return nil, err } @@ -241,7 +249,7 @@ func getAccountsHost() string { } func cacheResponse(response []byte) error { - var lrc = licenseResponseCache{ + lrc := licenseResponseCache{ Body: response, } diff --git a/pro/logic/relays.go b/pro/logic/relays.go index 95614dd6d..9cb5e836d 100644 --- a/pro/logic/relays.go +++ b/pro/logic/relays.go @@ -3,15 +3,32 @@ package logic import ( "errors" "fmt" + "net" + "github.com/gravitl/netmaker/logger" "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic/acls/nodeacls" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mq" "github.com/gravitl/netmaker/servercfg" - "net" + "golang.org/x/exp/slog" ) +// GetRelays - gets all the nodes that are relays +func GetRelays() ([]models.Node, error) { + nodes, err := logic.GetAllNodes() + if err != nil { + return nil, err + } + relays := make([]models.Node, 0) + for _, node := range nodes { + if node.IsRelay { + relays = append(relays, node) + } + } + return relays, nil +} + // CreateRelay - creates a relay func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) { var returnnodes []models.Node @@ -67,7 +84,7 @@ func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.N return returnnodes } -//func GetRelayedNodes(relayNode *models.Node) (models.Node, error) { +// func GetRelayedNodes(relayNode *models.Node) (models.Node, error) { // var returnnodes []models.Node // networkNodes, err := GetNetworkNodes(relayNode.Network) // if err != nil { @@ -81,12 +98,12 @@ func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.N // } // } // return returnnodes, nil -//} +// } // ValidateRelay - checks if relay is valid func ValidateRelay(relay models.RelayRequest) error { var err error - //isIp := functions.IsIpCIDR(gateway.RangeString) + // isIp := functions.IsIpCIDR(gateway.RangeString) empty := len(relay.RelayedNodes) == 0 if empty { return errors.New("IP Ranges Cannot Be Empty") @@ -136,7 +153,13 @@ func UpdateRelayed(currentNode, newNode *models.Node) { updatenodes := updateRelayNodes(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes) if len(updatenodes) > 0 { for _, relayedNode := range updatenodes { - mq.RunUpdates(&relayedNode, false) + node := relayedNode + go func() { + if err := mq.NodeUpdate(&node); err != nil { + slog.Error("error publishing node update to node", "node", node.ID, "error", err) + } + + }() } } } diff --git a/pro/remote_access_client.go b/pro/remote_access_client.go new file mode 100644 index 000000000..e66fc4de2 --- /dev/null +++ b/pro/remote_access_client.go @@ -0,0 +1,76 @@ +package pro + +import ( + "fmt" + "time" + + "github.com/gravitl/netmaker/logic" + "github.com/gravitl/netmaker/models" + "github.com/gravitl/netmaker/mq" + "github.com/gravitl/netmaker/servercfg" + "golang.org/x/exp/slog" +) + +const racAutoDisableCheckInterval = 3 * time.Minute + +// AddRacHooks - adds hooks for Remote Access Client +func AddRacHooks() { + slog.Debug("adding RAC autodisable hook") + logic.HookManagerCh <- models.HookDetails{ + Hook: racAutoDisableHook, + Interval: racAutoDisableCheckInterval, + } +} + +// racAutoDisableHook - checks if RAC is enabled and if it is, checks if it should be disabled +func racAutoDisableHook() error { + slog.Debug("running RAC autodisable hook") + + users, err := logic.GetUsers() + if err != nil { + slog.Error("error getting users: ", "error", err) + return err + } + clients, err := logic.GetAllExtClients() + if err != nil { + slog.Error("error getting clients: ", "error", err) + return err + } + + currentTime := time.Now() + validityDuration := servercfg.GetJwtValidityDuration() + for _, user := range users { + if !currentTime.After(user.LastLoginTime.Add(validityDuration)) { + continue + } + for _, client := range clients { + if (client.OwnerID == user.UserName) && !user.IsAdmin && !user.IsSuperAdmin && client.Enabled { + slog.Info(fmt.Sprintf("disabling ext client %s for user %s due to RAC autodisabling", client.ClientID, client.OwnerID)) + if err := disableExtClient(&client); err != nil { + slog.Error("error disabling ext client in RAC autodisable hook", "error", err) + continue // dont return but try for other clients + } + } + } + } + + slog.Debug("finished running RAC autodisable hook") + return nil +} + +func disableExtClient(client *models.ExtClient) error { + if newClient, err := logic.ToggleExtClientConnectivity(client, false); err != nil { + return err + } else { + // publish peer update to ingress gateway + if ingressNode, err := logic.GetNodeByID(newClient.IngressGatewayID); err == nil { + if err = mq.PublishPeerUpdate(); err != nil { + slog.Error("error updating ext clients on", "ingress", ingressNode.ID.String(), "err", err.Error()) + } + } else { + return err + } + } + + return nil +} diff --git a/pro/types.go b/pro/types.go index 97be71943..3e4ef08d1 100644 --- a/pro/types.go +++ b/pro/types.go @@ -54,13 +54,15 @@ type LicenseSecret struct { // Usage - struct for license usage type Usage struct { - Servers int `json:"servers"` - Users int `json:"users"` - Hosts int `json:"hosts"` - Clients int `json:"clients"` - Networks int `json:"networks"` - Ingresses int `json:"ingresses"` - Egresses int `json:"egresses"` + Servers int `json:"servers"` + Users int `json:"users"` + Hosts int `json:"hosts"` + Clients int `json:"clients"` + Networks int `json:"networks"` + Ingresses int `json:"ingresses"` + Egresses int `json:"egresses"` + Relays int `json:"relays"` + InternetGateways int `json:"internet_gateways"` } // Usage.SetDefaults - sets the default values for usage @@ -72,6 +74,8 @@ func (l *Usage) SetDefaults() { l.Networks = 0 l.Ingresses = 0 l.Egresses = 0 + l.Relays = 0 + l.InternetGateways = 0 } // ValidateLicenseRequest - used for request to validate license endpoint diff --git a/pro/util.go b/pro/util.go index f48418b17..7fc2d4cd9 100644 --- a/pro/util.go +++ b/pro/util.go @@ -16,9 +16,7 @@ func base64encode(input []byte) string { // base64decode - base64 decode helper function func base64decode(input string) []byte { - bytes, err := base64.StdEncoding.DecodeString(input) - if err != nil { return nil } @@ -44,6 +42,7 @@ func getCurrentServerUsage() (limits Usage) { if err == nil { limits.Networks = len(networks) } + // TODO this part bellow can be optimized to get nodes just once ingresses, err := logic.GetAllIngresses() if err == nil { limits.Ingresses = len(ingresses) @@ -52,5 +51,13 @@ func getCurrentServerUsage() (limits Usage) { if err == nil { limits.Egresses = len(egresses) } + relays, err := logic.GetRelays() + if err == nil { + limits.Relays = len(relays) + } + gateways, err := logic.GetInternetGateways() + if err == nil { + limits.InternetGateways = len(gateways) + } return } diff --git a/release.md b/release.md index 29ca258fd..db0687039 100644 --- a/release.md +++ b/release.md @@ -1,16 +1,11 @@ -# Netmaker v0.21.0 +# Netmaker v0.21.1 ## Whats New -- Sync clients with server state from UI - +- New User Management, Refer Docs For More Info +- Added Support For Remote Access Client ## What's Fixed -- Upgrade Process from v0.17.1 to latest version can be now done seamlessly, please refer docs for more information -- Expired nodes clean up is handled correctly now -- Ext client config generation fixed for ipv6 endpoints -- installation process will only generate certs required for required Domains based on CE or Pro -- support for ARM machines on install script - +- Proper Cleanup Of Extclients On a Client Gateway Deletion ## known issues - Windows installer does not install WireGuard - netclient-gui will continously display error dialog if netmaker server is offline diff --git a/scripts/netmaker.default.env b/scripts/netmaker.default.env index e714c19a1..fcacaeaae 100644 --- a/scripts/netmaker.default.env +++ b/scripts/netmaker.default.env @@ -19,47 +19,48 @@ NETMAKER_TENANT_ID= LICENSE_KEY= SERVER_IMAGE_TAG= UI_IMAGE_TAG= -NETCLIENT_ENDPOINT_DETECTION="disabled" +NETCLIENT_ENDPOINT_DETECTION=disabled # used for HA - identifies this server vs other servers -NODE_ID="netmaker-server-1" -METRICS_EXPORTER="off" -PROMETHEUS="off" +NODE_ID=netmaker-server-1 +METRICS_EXPORTER=off +PROMETHEUS=off # Enables DNS Mode, meaning all nodes will set hosts file for private dns settings -DNS_MODE="on" +DNS_MODE=on # Enable auto update of netclient ? ENUM:- enabled,disabled | default=enabled -NETCLIENT_AUTO_UPDATE="enabled" +NETCLIENT_AUTO_UPDATE=enabled # The HTTP API port for Netmaker. Used for API calls / communication from front end. # If changed, need to change port of BACKEND_URL for netmaker-ui. -API_PORT="8081" -EXPORTER_API_PORT="8085" +API_PORT=8081 +EXPORTER_API_PORT=8085 # The "allowed origin" for API requests. Change to restrict where API requests can come from with comma-separated # URLs. ex:- https://dashboard.netmaker.domain1.com,https://dashboard.netmaker.domain2.com -CORS_ALLOWED_ORIGIN="*" +CORS_ALLOWED_ORIGIN=* # Show keys permanently in UI (until deleted) as opposed to 1-time display. -DISPLAY_KEYS="on" +DISPLAY_KEYS=on # Database to use - sqlite, postgres, or rqlite -DATABASE="sqlite" +DATABASE=sqlite # The address of the mq server. If running from docker compose it will be "mq". Otherwise, need to input address. # If using "host networking", it will find and detect the IP of the mq container. -SERVER_BROKER_ENDPOINT="ws://mq:1883" # For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt` +# For EMQX websockets use `SERVER_BROKER_ENDPOINT=ws://mq:8083/mqtt` +SERVER_BROKER_ENDPOINT=ws://mq:1883 # The reachable port of STUN on the server -STUN_PORT="3478" +STUN_PORT=3478 # Logging verbosity level - 1, 2, or 3 -VERBOSITY="1" +VERBOSITY=1 # Port to access turn server -TURN_PORT="3479" +TURN_PORT=3479 # Config for using turn, accepts either true/false -USE_TURN="true" -DEBUG_MODE="off" -TURN_API_PORT="8089" +USE_TURN=true +DEBUG_MODE=off +TURN_API_PORT=8089 # Enables the REST backend (API running on API_PORT at SERVER_HTTP_HOST). # Change to "off" to turn off. -REST_BACKEND="on" +REST_BACKEND=on # If turned "on", Server will not set Host based on remote IP check. # This is already overridden if SERVER_HOST is set. Turned "off" by default. -DISABLE_REMOTE_IP_CHECK="off" +DISABLE_REMOTE_IP_CHECK=off # Whether or not to send telemetry data to help improve Netmaker. Switch to "off" to opt out of sending telemetry. -TELEMETRY="on" +TELEMETRY=on ### # # OAuth section @@ -77,3 +78,7 @@ FRONTEND_URL= AZURE_TENANT= # https://oidc.yourprovider.com - URL of oidc provider OIDC_ISSUER= +# Duration of JWT token validity in seconds +JWT_VALIDITY_DURATION=43200 +# Auto disable a user's connecteds clients bassed on JWT token expiration +RAC_AUTO_DISABLE="true" diff --git a/scripts/nm-certs.sh b/scripts/nm-certs.sh deleted file mode 100755 index 41122969d..000000000 --- a/scripts/nm-certs.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/bash - -CONFIG_FILE=netmaker.env -SCRIPT_DIR=$(dirname "$(realpath "$0")") - -# get and check the config -if [ ! -f "$SCRIPT_DIR/$CONFIG_FILE" ]; then - echo "Config file missing" - exit 1 -fi -source "$SCRIPT_DIR/$CONFIG_FILE" -if [ -z "$NM_DOMAIN" ] || [ -z "$NM_EMAIL" ]; then - echo "Config not valid" - exit 1 -fi - -# TODO make sure this doesnt break, parse `certbot certificates` if yes -CERT_DIR="$SCRIPT_DIR/letsencrypt/live/api.$NM_DOMAIN" - -echo "Setting up SSL certificates..." - -# preserve the env state -RESTART_CADDY=false -if [ -n "$(docker ps | grep caddy)" ]; then - echo "Caddy is running, stopping for now..." - RESTART_CADDY=true - docker-compose -f /root/docker-compose.yml stop caddy -fi - -if [ "$INSTALL_TYPE" = "ce" ]; then - CERTBOT_PARAMS=$(cat <"$SCRIPT_DIR/certbot-entry.sh" -#!/bin/sh -# deps -apk update -apk add bash curl -# zerossl -wget -qO zerossl-bot.sh "https://github.com/zerossl/zerossl-bot/raw/master/zerossl-bot.sh" -chmod +x zerossl-bot.sh -# request the certs -./zerossl-bot.sh "$CERTBOT_PARAMS" -EOF - -chmod +x "$SCRIPT_DIR/certbot-entry.sh" - -# request certs -sudo docker run -it --rm --name certbot \ - -p 80:80 -p 443:443 \ - -v "$SCRIPT_DIR/certbot-entry.sh:/opt/certbot/certbot-entry.sh" \ - -v "$SCRIPT_DIR/letsencrypt:/etc/letsencrypt" \ - --entrypoint "/opt/certbot/certbot-entry.sh" \ - certbot/certbot - -# clean up -rm "$SCRIPT_DIR/certbot-entry.sh" - -# check if successful -if [ ! -f "$CERT_DIR"/fullchain.pem ]; then - # fallback to letsencrypt-certbot - sudo docker run -it --rm --name certbot \ - -p 80:80 -p 443:443 \ - -v "$SCRIPT_DIR/letsencrypt:/etc/letsencrypt" \ - certbot/certbot $CERTBOT_PARAMS - if [ ! -f "$CERT_DIR"/fullchain.pem ]; then - echo "Missing file: $CERT_DIR/fullchain.pem" - echo "SSL certificates failed" - exit 1 - fi -fi - -# copy for mounting -mkdir -p certs -cp -L "$CERT_DIR/fullchain.pem" "$SCRIPT_DIR/certs/fullchain.pem" -cp -L "$CERT_DIR/privkey.pem" "$SCRIPT_DIR/certs/privkey.pem" - -echo "SSL certificates ready" - -# preserve the env state -if [ "$RESTART_CADDY" = true ]; then - echo "Starting Caddy..." - docker-compose -f /root/docker-compose.yml start caddy -fi - -# install crontab -ln -sfn "$SCRIPT_DIR"/nm-certs.sh /etc/cron.monthly/nm-certs.sh diff --git a/scripts/nm-quick.sh b/scripts/nm-quick.sh index 491e339b1..714bb360c 100755 --- a/scripts/nm-quick.sh +++ b/scripts/nm-quick.sh @@ -310,7 +310,7 @@ save_config() { ( "CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" - "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT") + "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE") for name in "${toCopy[@]}"; do save_config_item $name "${!name}" done @@ -759,7 +759,6 @@ install_netmaker() { wget -qO "$SCRIPT_DIR"/Caddyfile "$CADDY_URL" wget -qO "$SCRIPT_DIR"/netmaker.default.env "$BASE_URL/scripts/netmaker.default.env" wget -qO "$SCRIPT_DIR"/mosquitto.conf "$BASE_URL/docker/mosquitto.conf" - wget -qO "$SCRIPT_DIR"/nm-certs.sh "$BASE_URL/scripts/nm-certs.sh" wget -qO "$SCRIPT_DIR"/wait.sh "$BASE_URL/docker/wait.sh" fi @@ -770,10 +769,6 @@ install_netmaker() { ln -fs "$SCRIPT_DIR/netmaker.env" "$SCRIPT_DIR/.env" save_config - # Fetch / update certs using certbot - chmod +x "$SCRIPT_DIR"/nm-certs.sh - "$SCRIPT_DIR"/nm-certs.sh - echo "Starting containers..." # increase the timeouts diff --git a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh index 7ece935ff..79a78bd5a 100644 --- a/scripts/nm-upgrade-0-17-1-to-0-19-0.sh +++ b/scripts/nm-upgrade-0-17-1-to-0-19-0.sh @@ -1,6 +1,6 @@ #!/bin/bash -LATEST="v0.21.0" +LATEST="v0.21.1" INSTALL_PATH="/root" trap restore_old_netmaker_instructions diff --git a/scripts/nm-upgrade.sh b/scripts/nm-upgrade.sh index b4bb68a24..3a62bc4f6 100755 --- a/scripts/nm-upgrade.sh +++ b/scripts/nm-upgrade.sh @@ -180,7 +180,7 @@ save_config() { ( "CORS_ALLOWED_ORIGIN" "DISPLAY_KEYS" "DATABASE" "SERVER_BROKER_ENDPOINT" "STUN_PORT" "VERBOSITY" "TURN_PORT" "USE_TURN" "DEBUG_MODE" "TURN_API_PORT" "REST_BACKEND" "DISABLE_REMOTE_IP_CHECK" "NETCLIENT_ENDPOINT_DETECTION" "TELEMETRY" "AUTH_PROVIDER" "CLIENT_ID" "CLIENT_SECRET" - "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT") + "FRONTEND_URL" "AZURE_TENANT" "OIDC_ISSUER" "EXPORTER_API_PORT" "JWT_VALIDITY_DURATION" "RAC_AUTO_DISABLE") for name in "${toCopy[@]}"; do save_config_item $name "${!name}" done diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index c0b2341cf..a7347aeb2 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -85,17 +85,36 @@ func GetServerConfig() config.ServerConfig { cfg.FrontendURL = GetFrontendURL() cfg.Telemetry = Telemetry() cfg.Server = GetServer() - cfg.StunList = GetStunListString() cfg.Verbosity = GetVerbosity() cfg.IsPro = "no" if IsPro { cfg.IsPro = "yes" } + cfg.JwtValidityDuration = GetJwtValidityDuration() + cfg.RacAutoDisable = GetRacAutoDisable() return cfg } -// GetServerConfig - gets the server config into memory from file or env +// GetJwtValidityDuration - returns the JWT validity duration in seconds +func GetJwtValidityDuration() time.Duration { + var defaultDuration = time.Duration(24) * time.Hour + if os.Getenv("JWT_VALIDITY_DURATION") != "" { + t, err := strconv.Atoi(os.Getenv("JWT_VALIDITY_DURATION")) + if err != nil { + return defaultDuration + } + return time.Duration(t) * time.Second + } + return defaultDuration +} + +// GetRacAutoDisable - returns whether the feature to autodisable RAC is enabled +func GetRacAutoDisable() bool { + return os.Getenv("RAC_AUTO_DISABLE") == "true" +} + +// GetServerInfo - gets the server config into memory from file or env func GetServerInfo() models.ServerConfig { var cfg models.ServerConfig cfg.Server = GetServer() @@ -112,7 +131,6 @@ func GetServerInfo() models.ServerConfig { cfg.Version = GetVersion() cfg.IsPro = IsPro cfg.StunPort = GetStunPort() - cfg.StunList = GetStunList() cfg.TurnDomain = GetTurnHost() cfg.TurnPort = GetTurnPort() cfg.UseTurn = IsUsingTurn() @@ -223,46 +241,6 @@ func GetAPIPort() string { return apiport } -// GetStunList - gets the stun servers -func GetStunList() []models.StunServer { - stunList := []models.StunServer{ - { - Domain: "stun1.netmaker.io", - Port: 3478, - }, - { - Domain: "stun2.netmaker.io", - Port: 3478, - }, - } - parsed := false - if os.Getenv("STUN_LIST") != "" { - stuns, err := parseStunList(os.Getenv("STUN_LIST")) - if err == nil { - parsed = true - stunList = stuns - } - } - if !parsed && config.Config.Server.StunList != "" { - stuns, err := parseStunList(config.Config.Server.StunList) - if err == nil { - stunList = stuns - } - } - return stunList -} - -// GetStunList - gets the stun servers w/o parsing to struct -func GetStunListString() string { - stunList := "stun1.netmaker.io:3478,stun2.netmaker.io:3478" - if os.Getenv("STUN_LIST") != "" { - stunList = os.Getenv("STUN_LIST") - } else if config.Config.Server.StunList != "" { - stunList = config.Config.Server.StunList - } - return stunList -} - // GetCoreDNSAddr - gets the core dns address func GetCoreDNSAddr() string { addr, _ := GetPublicIP() @@ -784,33 +762,3 @@ func GetEnvironment() string { } return "" } - -// parseStunList - turn string into slice of StunServers -func parseStunList(stunString string) ([]models.StunServer, error) { - var err error - stunServers := []models.StunServer{} - stuns := strings.Split(stunString, ",") - if len(stuns) == 0 { - return stunServers, errors.New("no stun servers provided") - } - for _, stun := range stuns { - stun = strings.Trim(stun, " ") - stunInfo := strings.Split(stun, ":") - if len(stunInfo) != 2 { - continue - } - port, err := strconv.Atoi(stunInfo[1]) - if err != nil || port == 0 { - continue - } - stunServers = append(stunServers, models.StunServer{ - Domain: stunInfo[0], - Port: port, - }) - - } - if len(stunServers) == 0 { - err = errors.New("no stun entries parsable") - } - return stunServers, err -} diff --git a/swagger.yaml b/swagger.yml similarity index 65% rename from swagger.yaml rename to swagger.yml index 77c7de77b..2637cdb10 100644 --- a/swagger.yaml +++ b/swagger.yml @@ -15,34 +15,90 @@ definitions: description: ACLContainer - the total list of all node's ACL in a given network type: object x-go-package: github.com/gravitl/netmaker/logic/acls - AccessKey: - description: AccessKey - access key struct + ApiHost: + description: ApiHost - the host struct for API usage properties: - accessstring: + debug: + type: boolean + x-go-name: Debug + defaultinterface: + type: string + x-go-name: DefaultInterface + endpointip: type: string - x-go-name: AccessString + x-go-name: EndpointIP + firewallinuse: + type: string + x-go-name: FirewallInUse + id: + type: string + x-go-name: ID + interfaces: + items: + $ref: '#/definitions/Iface' + type: array + x-go-name: Interfaces + isdefault: + type: boolean + x-go-name: IsDefault + isrelay: + type: boolean + x-go-name: IsRelay + isrelayed: + type: boolean + x-go-name: IsRelayed + isstatic: + type: boolean + x-go-name: IsStatic + listenport: + format: int64 + type: integer + x-go-name: ListenPort + macaddress: + type: string + x-go-name: MacAddress + mtu: + format: int64 + type: integer + x-go-name: MTU name: type: string x-go-name: Name - uses: + nat_type: + type: string + x-go-name: NatType + nodes: + items: + type: string + type: array + x-go-name: Nodes + os: + type: string + x-go-name: OS + publickey: + type: string + x-go-name: PublicKey + relay_hosts: + items: + type: string + type: array + x-go-name: RelayedHosts + relayed_by: + type: string + x-go-name: RelayedBy + verbosity: format: int64 type: integer - x-go-name: Uses - value: + x-go-name: Verbosity + version: type: string - x-go-name: Value + x-go-name: Version + wg_public_listen_port: + format: int64 + type: integer + x-go-name: WgPublicListenPort type: object x-go-package: github.com/gravitl/netmaker/models - AttributeTypeAndValue: - description: |- - AttributeTypeAndValue mirrors the ASN.1 structure of the same name in - RFC 5280, Section 4.1.2.4. - properties: - Type: - $ref: '#/definitions/ObjectIdentifier' - Value: {} - type: object - x-go-package: crypto/x509/pkix AuthParams: description: AuthParams - struct for auth params properties: @@ -63,6 +119,28 @@ definitions: clientid: type: string x-go-name: ClientID + deniednodeacls: + additionalProperties: + type: object + type: object + x-go-name: DeniedACLs + dns: + type: string + x-go-name: DNS + enabled: + type: boolean + x-go-name: Enabled + extraallowedips: + items: + type: string + type: array + x-go-name: ExtraAllowedIPs + publickey: + type: string + x-go-name: PublicKey + remote_access_client_id: + type: string + x-go-name: RemoteAccessClientID type: object x-go-package: github.com/gravitl/netmaker/models DNSEntry: @@ -93,9 +171,6 @@ definitions: EgressGatewayRequest: description: EgressGatewayRequest - egress gateway request properties: - interface: - type: string - x-go-name: Interface natenabled: type: string x-go-name: NatEnabled @@ -112,6 +187,40 @@ definitions: x-go-name: Ranges type: object x-go-package: github.com/gravitl/netmaker/models + EnrollmentKey: + description: EnrollmentKey - the key used to register hosts and join them to specific networks + properties: + expiration: + format: date-time + type: string + x-go-name: Expiration + networks: + items: + type: string + type: array + x-go-name: Networks + tags: + items: + type: string + type: array + x-go-name: Tags + token: + type: string + x-go-name: Token + type: + $ref: '#/definitions/KeyType' + unlimited: + type: boolean + x-go-name: Unlimited + uses_remaining: + format: int64 + type: integer + x-go-name: UsesRemaining + value: + type: string + x-go-name: Value + type: object + x-go-package: github.com/gravitl/netmaker/models ExtClient: description: ExtClient - struct for external clients properties: @@ -124,12 +233,22 @@ definitions: clientid: type: string x-go-name: ClientID - description: - type: string - x-go-name: Description + deniednodeacls: + additionalProperties: + type: object + type: object + x-go-name: DeniedACLs + dns: + type: string + x-go-name: DNS enabled: type: boolean x-go-name: Enabled + extraallowedips: + items: + type: string + type: array + x-go-name: ExtraAllowedIPs ingressgatewayendpoint: type: string x-go-name: IngressGatewayEndpoint @@ -143,158 +262,227 @@ definitions: network: type: string x-go-name: Network + ownerid: + type: string + x-go-name: OwnerID privatekey: type: string x-go-name: PrivateKey publickey: type: string x-go-name: PublicKey + remote_access_client_id: + type: string + x-go-name: RemoteAccessClientID type: object x-go-package: github.com/gravitl/netmaker/models - IPMask: - description: See type IPNet and func ParseCIDR for details. + File: + title: File represents an open file descriptor. + type: object + x-go-package: os + HardwareAddr: items: format: uint8 type: integer - title: |- - An IPMask is a bitmask that can be used to manipulate - IP addresses for IP addressing and routing. + title: A HardwareAddr represents a physical hardware address. type: array x-go-package: net - IPNet: + Host: + description: Host - represents a host on the network properties: - IP: + autoupdate: + type: boolean + x-go-name: AutoUpdate + daemoninstalled: + type: boolean + x-go-name: DaemonInstalled + debug: + type: boolean + x-go-name: Debug + defaultinterface: type: string - Mask: - $ref: '#/definitions/IPMask' - title: An IPNet represents an IP network. - type: object - x-go-package: net - Key: - description: |- - A Key is a public, private, or pre-shared secret key. The Key constructor - functions in this package can be used to create Keys suitable for each of - these applications. - items: - format: uint8 - type: integer - type: array - x-go-package: golang.zx2c4.com/wireguard/wgctrl/wgtypes - Name: - description: |- - Name represents an X.509 distinguished name. This only includes the common - elements of a DN. Note that Name is only an approximation of the X.509 - structure. If an accurate representation is needed, asn1.Unmarshal the raw - subject or issuer as an RDNSequence. - properties: - Country: - items: - type: string - type: array - x-go-name: OrganizationalUnit - ExtraNames: - description: |- - ExtraNames contains attributes to be copied, raw, into any marshaled - distinguished names. Values override any attributes with the same OID. - The ExtraNames field is not populated when parsing, see Names. + x-go-name: DefaultInterface + endpointip: + type: string + x-go-name: EndpointIP + firewallinuse: + type: string + x-go-name: FirewallInUse + hostpass: + type: string + x-go-name: HostPass + id: + format: uuid + type: string + x-go-name: ID + interface: + type: string + x-go-name: Interface + interfaces: items: - $ref: '#/definitions/AttributeTypeAndValue' + $ref: '#/definitions/Iface' type: array - Locality: + x-go-name: Interfaces + ipforwarding: + type: boolean + x-go-name: IPForwarding + isdefault: + type: boolean + x-go-name: IsDefault + isdocker: + type: boolean + x-go-name: IsDocker + isk8s: + type: boolean + x-go-name: IsK8S + isstatic: + type: boolean + x-go-name: IsStatic + listenport: + format: int64 + type: integer + x-go-name: ListenPort + macaddress: + $ref: '#/definitions/HardwareAddr' + mtu: + format: int64 + type: integer + x-go-name: MTU + name: + type: string + x-go-name: Name + nat_type: + type: string + x-go-name: NatType + nodes: items: type: string type: array - x-go-name: Province - Names: - description: |- - Names contains all parsed attributes. When parsing distinguished names, - this can be used to extract non-standard attributes that are not parsed - by this package. When marshaling to RDNSequences, the Names field is - ignored, see ExtraNames. - items: - $ref: '#/definitions/AttributeTypeAndValue' - type: array - SerialNumber: + x-go-name: Nodes + os: type: string - x-go-name: CommonName - StreetAddress: + x-go-name: OS + publickey: + $ref: '#/definitions/Key' + traffickeypublic: items: - type: string + format: uint8 + type: integer type: array - x-go-name: PostalCode + x-go-name: TrafficKeyPublic + turn_endpoint: + type: string + x-go-name: TurnEndpoint + verbosity: + format: int64 + type: integer + x-go-name: Verbosity + version: + type: string + x-go-name: Version + wg_public_listen_port: + format: int64 + type: integer + x-go-name: WgPublicListenPort type: object - x-go-package: crypto/x509/pkix - Network: - description: |- - Network Struct - contains info for a given unique network - At some point, need to replace all instances of Name with something else like Identifier + x-go-package: github.com/gravitl/netmaker/models + HostPull: + description: HostPull - response of a host's pull properties: - accesskeys: + host: + $ref: '#/definitions/Host' + nodes: items: - $ref: '#/definitions/AccessKey' + $ref: '#/definitions/Node' type: array - x-go-name: AccessKeys - addressrange: - type: string - x-go-name: AddressRange - addressrange6: - type: string - x-go-name: AddressRange6 - allowmanualsignup: - type: string - x-go-name: AllowManualSignUp - defaultacl: + x-go-name: Nodes + peer_ids: + $ref: '#/definitions/PeerMap' + peers: + items: + $ref: '#/definitions/PeerConfig' + type: array + x-go-name: Peers + server_config: + $ref: '#/definitions/ServerConfig' + type: object + x-go-package: github.com/gravitl/netmaker/models + IDandAddr: + description: IDandAddr - struct to hold ID and primary Address + properties: + address: type: string - x-go-name: DefaultACL - defaultextclientdns: + x-go-name: Address + id: type: string - x-go-name: DefaultExtClientDNS - defaultinterface: + x-go-name: ID + is_extclient: + type: boolean + x-go-name: IsExtClient + isserver: type: string - x-go-name: DefaultInterface - defaultkeepalive: - format: int32 - type: integer - x-go-name: DefaultKeepalive - defaultlistenport: - format: int32 - type: integer - x-go-name: DefaultListenPort - defaultmtu: - format: int32 + x-go-name: IsServer + listen_port: + format: int64 type: integer - x-go-name: DefaultMTU - defaultudpholepunch: + x-go-name: ListenPort + name: type: string - x-go-name: DefaultUDPHolePunch - isipv4: + x-go-name: Name + network: type: string - x-go-name: IsIPv4 - isipv6: + x-go-name: Network + type: object + x-go-package: github.com/gravitl/netmaker/models + IPMask: + description: See type IPNet and func ParseCIDR for details. + items: + format: uint8 + type: integer + title: |- + An IPMask is a bitmask that can be used to manipulate + IP addresses for IP addressing and routing. + type: array + x-go-package: net + IPNet: + properties: + IP: type: string - x-go-name: IsIPv6 - ispointtosite: + Mask: + $ref: '#/definitions/IPMask' + title: An IPNet represents an IP network. + type: object + x-go-package: net + Iface: + description: Iface struct for local interfaces of a node + properties: + address: + $ref: '#/definitions/IPNet' + addressString: type: string - x-go-name: IsPointToSite - netid: + x-go-name: AddressString + name: type: string - x-go-name: NetID - networklastmodified: - format: int64 - type: integer - x-go-name: NetworkLastModified - nodelimit: - format: int32 - type: integer - x-go-name: NodeLimit - nodeslastmodified: - format: int64 - type: integer - x-go-name: NodesLastModified + x-go-name: Name type: object x-go-package: github.com/gravitl/netmaker/models - Node: - description: Node - struct for node model + Key: + description: |- + A Key is a public, private, or pre-shared secret key. The Key constructor + functions in this package can be used to create Keys suitable for each of + these applications. + items: + format: uint8 + type: integer + type: array + x-go-package: golang.zx2c4.com/wireguard/wgctrl/wgtypes + KeyType: + description: KeyType - the type of enrollment key + format: int64 + type: integer + x-go-package: github.com/gravitl/netmaker/models + LegacyNode: + description: LegacyNode - legacy struct for node model properties: accesskey: type: string @@ -316,6 +504,10 @@ definitions: connected: type: string x-go-name: Connected + defaultacl: + description: == PRO == + type: string + x-go-name: DefaultACL dnson: type: string x-go-name: DNSOn @@ -336,6 +528,12 @@ definitions: format: int64 type: integer x-go-name: ExpirationDateTime + failover: + type: string + x-go-name: Failover + failovernode: + type: string + x-go-name: FailoverNode firewallinuse: type: string x-go-name: FirewallInUse @@ -351,6 +549,11 @@ definitions: interface: type: string x-go-name: Interface + interfaces: + items: + $ref: '#/definitions/Iface' + type: array + x-go-name: Interfaces internetgateway: type: string x-go-name: InternetGateway @@ -420,64 +623,221 @@ definitions: x-go-name: MTU name: type: string - x-go-name: Name + x-go-name: Name + network: + type: string + x-go-name: Network + networksettings: + $ref: '#/definitions/Network' + os: + type: string + x-go-name: OS + ownerid: + type: string + x-go-name: OwnerID + password: + type: string + x-go-name: Password + persistentkeepalive: + format: int32 + type: integer + x-go-name: PersistentKeepalive + publickey: + type: string + x-go-name: PublicKey + relayaddrs: + items: + type: string + type: array + x-go-name: RelayAddrs + server: + type: string + x-go-name: Server + traffickeys: + $ref: '#/definitions/TrafficKeys' + udpholepunch: + type: string + x-go-name: UDPHolePunch + version: + type: string + x-go-name: Version + type: object + x-go-package: github.com/gravitl/netmaker/models + Network: + description: |- + Network Struct - contains info for a given unique network + At some point, need to replace all instances of Name with something else like Identifier + properties: + addressrange: + type: string + x-go-name: AddressRange + addressrange6: + type: string + x-go-name: AddressRange6 + allowmanualsignup: + type: string + x-go-name: AllowManualSignUp + defaultacl: + type: string + x-go-name: DefaultACL + defaultinterface: + type: string + x-go-name: DefaultInterface + defaultkeepalive: + format: int32 + type: integer + x-go-name: DefaultKeepalive + defaultlistenport: + format: int32 + type: integer + x-go-name: DefaultListenPort + defaultmtu: + format: int32 + type: integer + x-go-name: DefaultMTU + defaultpostdown: + type: string + x-go-name: DefaultPostDown + defaultudpholepunch: + type: string + x-go-name: DefaultUDPHolePunch + isipv4: + type: string + x-go-name: IsIPv4 + isipv6: + type: string + x-go-name: IsIPv6 + netid: + type: string + x-go-name: NetID + networklastmodified: + format: int64 + type: integer + x-go-name: NetworkLastModified + nodelimit: + format: int32 + type: integer + x-go-name: NodeLimit + nodeslastmodified: + format: int64 + type: integer + x-go-name: NodesLastModified + type: object + x-go-package: github.com/gravitl/netmaker/models + Node: + description: Node - a model of a network node + properties: + action: + type: string + x-go-name: Action + address: + $ref: '#/definitions/IPNet' + address6: + $ref: '#/definitions/IPNet' + connected: + type: boolean + x-go-name: Connected + defaultacl: + description: == PRO == + type: string + x-go-name: DefaultACL + dnson: + type: boolean + x-go-name: DNSOn + egressgatewaynatenabled: + type: boolean + x-go-name: EgressGatewayNatEnabled + egressgatewayranges: + items: + type: string + type: array + x-go-name: EgressGatewayRanges + egressgatewayrequest: + $ref: '#/definitions/EgressGatewayRequest' + expdatetime: + format: date-time + type: string + x-go-name: ExpirationDateTime + failover: + type: boolean + x-go-name: Failover + failovernode: + format: uuid + type: string + x-go-name: FailoverNode + hostid: + format: uuid + type: string + x-go-name: HostID + id: + format: uuid + type: string + x-go-name: ID + ingressdns: + type: string + x-go-name: IngressDNS + ingressgatewayrange: + type: string + x-go-name: IngressGatewayRange + ingressgatewayrange6: + type: string + x-go-name: IngressGatewayRange6 + internetgateway: + $ref: '#/definitions/UDPAddr' + isegressgateway: + type: boolean + x-go-name: IsEgressGateway + isingressgateway: + type: boolean + x-go-name: IsIngressGateway + isrelay: + type: boolean + x-go-name: IsRelay + isrelayed: + type: boolean + x-go-name: IsRelayed + lastcheckin: + format: date-time + type: string + x-go-name: LastCheckIn + lastmodified: + format: date-time + type: string + x-go-name: LastModified + lastpeerupdate: + format: date-time + type: string + x-go-name: LastPeerUpdate + localaddress: + $ref: '#/definitions/IPNet' network: type: string x-go-name: Network - networksettings: - $ref: '#/definitions/Network' - os: - type: string - x-go-name: OS - password: - type: string - x-go-name: Password + networkrange: + $ref: '#/definitions/IPNet' + networkrange6: + $ref: '#/definitions/IPNet' + ownerid: + type: string + x-go-name: OwnerID + pendingdelete: + type: boolean + x-go-name: PendingDelete persistentkeepalive: - format: int32 - type: integer - x-go-name: PersistentKeepalive - publickey: + $ref: '#/definitions/Duration' + relayedby: type: string - x-go-name: PublicKey - relayaddrs: + x-go-name: RelayedBy + relaynodes: items: type: string type: array - x-go-name: RelayAddrs + x-go-name: RelayedNodes server: type: string x-go-name: Server - traffickeys: - $ref: '#/definitions/TrafficKeys' - udpholepunch: - type: string - x-go-name: UDPHolePunch - version: - type: string - x-go-name: Version - type: object - x-go-package: github.com/gravitl/netmaker/models - NodeGet: - description: NodeGet - struct for a single node get response - properties: - node: - $ref: '#/definitions/Node' - peers: - items: - $ref: '#/definitions/PeerConfig' - type: array - x-go-name: Peers - serverconfig: - $ref: '#/definitions/ServerConfig' type: object x-go-package: github.com/gravitl/netmaker/models - ObjectIdentifier: - items: - format: int64 - type: integer - title: An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. - type: array - x-go-package: encoding/asn1 PeerConfig: description: |- Because the zero value of some Go types may be significant to WireGuard for @@ -518,22 +878,21 @@ definitions: title: A PeerConfig is a WireGuard device peer configuration. type: object x-go-package: golang.zx2c4.com/wireguard/wgctrl/wgtypes - PrivateKey: - items: - format: uint8 - type: integer - title: PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer. - type: array - x-go-package: crypto/ed25519 - RegisterRequest: - description: RegisterRequest - struct for registation with netmaker server + PeerMap: + additionalProperties: + $ref: '#/definitions/IDandAddr' + description: PeerMap - peer map for ids and addresses in metrics + type: object + x-go-package: github.com/gravitl/netmaker/models + RegisterResponse: + description: RegisterResponse - the response to a successful enrollment register properties: - CommonName: - $ref: '#/definitions/Name' - Key: - $ref: '#/definitions/PrivateKey' + requested_host: + $ref: '#/definitions/Host' + server_config: + $ref: '#/definitions/ServerConfig' type: object - x-go-package: github.com/gravitl/netmaker/netclient/config + x-go-package: github.com/gravitl/netmaker/models RelayRequest: description: RelayRequest - relay request struct properties: @@ -547,7 +906,7 @@ definitions: items: type: string type: array - x-go-name: RelayAddrs + x-go-name: RelayedNodes type: object x-go-package: github.com/gravitl/netmaker/models ServerConfig: @@ -565,6 +924,12 @@ definitions: type: string AzureTenant: type: string + BasicAuth: + type: string + Broker: + type: string + BrokerType: + type: string ClientID: type: string ClientSecret: @@ -577,34 +942,59 @@ definitions: type: string Database: type: string + DeployedByOperator: + type: boolean DisableRemoteIPCheck: type: string DisplayKeys: type: string + EgressesLimit: + format: int64 + type: integer + EmqxRestEndpoint: + type: string + Environment: + type: string FrontendURL: type: string HostNetwork: type: string - MQHOST: + IngressesLimit: + format: int64 + type: integer + IsEE: type: string - MQPort: + x-go-name: IsPro + LicenseValue: type: string - MQServerPort: + MQPassword: type: string - ManageIPTables: + MQUserName: type: string + MachinesLimit: + format: int64 + type: integer MasterKey: type: string MessageQueueBackend: type: string + MetricsExporter: + type: string + NetclientAutoUpdate: + type: string + NetclientEndpointDetection: + type: string + NetmakerTenantID: + type: string + NetworksLimit: + format: int64 + type: integer NodeID: type: string OIDCIssuer: type: string Platform: type: string - PortForwardServices: - type: string PublicIPService: type: string RestBackend: @@ -613,8 +1003,31 @@ definitions: type: string Server: type: string + ServerBrokerEndpoint: + type: string + StunList: + type: string + StunPort: + format: int64 + type: integer Telemetry: type: string + TurnApiServer: + type: string + TurnPassword: + type: string + TurnPort: + format: int64 + type: integer + TurnServer: + type: string + TurnUserName: + type: string + UseTurn: + type: boolean + UsersLimit: + format: int64 + type: integer Verbosity: format: int32 type: integer @@ -622,6 +1035,36 @@ definitions: type: string type: object x-go-package: github.com/gravitl/netmaker/config + Signal: + description: Signal - struct for signalling peer + properties: + action: + $ref: '#/definitions/SignalAction' + from_host_pubkey: + type: string + x-go-name: FromHostPubKey + reply: + type: boolean + x-go-name: Reply + server: + type: string + x-go-name: Server + timestamp: + format: int64 + type: integer + x-go-name: TimeStamp + to_host_pubkey: + type: string + x-go-name: ToHostPubKey + turn_relay_addr: + type: string + x-go-name: TurnRelayEndpoint + type: object + x-go-package: github.com/gravitl/netmaker/models + SignalAction: + description: SignalAction - turn peer signal action + type: string + x-go-package: github.com/gravitl/netmaker/models SuccessResponse: properties: Code: @@ -668,14 +1111,17 @@ definitions: isadmin: type: boolean x-go-name: IsAdmin - networks: - items: - type: string - type: array - x-go-name: Networks + issuperadmin: + type: boolean + x-go-name: IsSuperAdmin password: type: string x-go-name: Password + remote_gw_ids: + additionalProperties: + type: object + type: object + x-go-name: RemoteGwIDs username: type: string x-go-name: UserName @@ -692,19 +1138,18 @@ definitions: x-go-name: UserName type: object x-go-package: github.com/gravitl/netmaker/models -host: netmaker.io +host: api.demo.netmaker.io info: description: |- - API Usage + # API Usage Most actions that can be performed via API can be performed via UI. We recommend managing your networks using the official netmaker-ui project. However, Netmaker can also be run without the UI, and all functions can be achieved via API calls. If your use case requires using Netmaker without the UI or you need to do some troubleshooting/advanced configuration, using the API directly may help. - - Authentication + # Authentication API calls must be authenticated via a header of the format -H “Authorization: Bearer ” There are two methods to obtain YOUR_SECRET_KEY: 1. Using the masterkey. By default, this value is “secret key,” but you should change this on your instance and keep it secure. This value can be set via env var at startup or in a config file (config/environments/< env >.yaml). See the [Netmaker](https://docs.netmaker.org/index.html) documentation for more details. 2. Using a JWT received for a node. This can be retrieved by calling the /api/nodes//authenticate endpoint, as documented below. title: Netmaker - version: 0.21.0 + version: 0.21.1 paths: /api/dns: get: @@ -813,6 +1258,9 @@ paths: required: true type: string x-go-name: Network + responses: + "200": + $ref: '#/responses/dnsResponse' schemes: - https summary: Gets node DNS entries associated with a network. @@ -823,7 +1271,7 @@ paths: operationId: pushDNS responses: "200": - $ref: '#/responses/dnsStringJSONResponse' + $ref: '#/responses/dnsResponse' schemes: - https summary: Push DNS entries to nameserver. @@ -948,6 +1396,12 @@ paths: get: operationId: getExtClientConf parameters: + - description: Type + in: path + name: type + required: true + type: string + x-go-name: Type - description: Client ID in: path name: clientid @@ -980,7 +1434,7 @@ paths: x-go-name: Network - description: Node ID in: path - name: node + name: nodeid required: true type: string x-go-name: NodeID @@ -990,6 +1444,9 @@ paths: schema: $ref: '#/definitions/CustomExtClient' x-go-name: CustomExtClient + responses: + "200": + $ref: '#/responses/okResponse' schemes: - https summary: Create an individual extclient. Must have valid key and be unique. @@ -1006,228 +1463,306 @@ paths: summary: Get the current public IP address. tags: - ipservice - /api/networks: + /api/hosts: get: - operationId: getNetworks - parameters: - - description: 'name: networks' - in: header - items: - type: string - name: networks - type: array - x-go-name: Networks + operationId: getHosts responses: "200": - $ref: '#/responses/getNetworksSliceResponse' + $ref: '#/responses/apiHostResponse' schemes: - https - summary: Lists all networks. + summary: Lists all hosts. tags: - - networks - post: - operationId: createNetwork + - hosts + /api/hosts/{hostid}: + delete: + operationId: deleteHost parameters: - - description: Network - in: body - name: network - schema: - $ref: '#/definitions/Network' - x-go-name: Network + - description: HostID + in: path + name: hostid + required: true + type: string + x-go-name: HostID responses: "200": - $ref: '#/responses/networkBodyResponse' + $ref: '#/responses/apiHostResponse' schemes: - https - summary: Create a network. + summary: Deletes a Netclient host from Netmaker server. tags: - - networks - /api/networks/{networkname}: - delete: - operationId: deleteNetwork + - hosts + put: + operationId: updateHost parameters: - - description: Network Name + - description: HostID in: path - name: networkname + name: hostid required: true type: string - x-go-name: NetworkName + x-go-name: HostID responses: "200": - $ref: '#/responses/stringJSONResponse' + $ref: '#/responses/apiHostResponse' schemes: - https - summary: Delete a network. Will not delete if there are any nodes that belong to the network. + summary: Updates a Netclient host on Netmaker server. tags: - - networks - get: - operationId: getNetwork + - hosts + /api/hosts/{hostid}/networks/{network}: + delete: + operationId: deleteHostFromNetwork parameters: - - description: Network Name + - description: hostid to add or delete from network in: path - name: networkname + name: hostid + required: true + type: string + x-go-name: HostID + - description: network + in: path + name: network required: true type: string - x-go-name: NetworkName + x-go-name: Network responses: "200": - $ref: '#/responses/networkBodyResponse' + $ref: '#/responses/okResponse' schemes: - https - summary: Get a network. + summary: Given a network, a host is removed from the network. tags: - - networks - put: - operationId: updateNetwork + - hosts + post: + operationId: addHostToNetwork parameters: - - description: Network - in: body + - description: hostid to add or delete from network + in: path + name: hostid + required: true + type: string + x-go-name: HostID + - description: network + in: path name: network - schema: - $ref: '#/definitions/Network' + required: true + type: string x-go-name: Network - - description: Network Name + responses: + "200": + $ref: '#/responses/okResponse' + schemes: + - https + summary: Given a network, a host is added to the network. + tags: + - hosts + /api/hosts/{hostid}/signalpeer: + post: + operationId: signalPeer + parameters: + - description: HostID in: path - name: networkname + name: hostid required: true type: string - x-go-name: NetworkName + x-go-name: HostID responses: "200": - $ref: '#/responses/networkBodyResponse' + $ref: '#/responses/signal' schemes: - https - summary: Update a network. + summary: send signal to peer. tags: - - networks - /api/networks/{networkname}/acls: - get: - operationId: getNetworkACL + - hosts + /api/hosts/{hostid}/sync: + post: + operationId: synchost parameters: - - description: Network Name + - description: HostID in: path - name: networkname + name: hostid required: true type: string - x-go-name: NetworkName - - description: ACL Container - in: body - name: acl_container - schema: - $ref: '#/definitions/ACLContainer' - x-go-name: ACLContainer + x-go-name: HostID responses: "200": - $ref: '#/responses/aclContainerResponse' + $ref: '#/responses/networkBodyResponse' schemes: - https - summary: Get a network ACL (Access Control List). + summary: Requests a host to pull. tags: - - networks - put: - operationId: updateNetworkACL + - hosts + /api/hosts/{hostid}keys: + post: + operationId: updateKeys parameters: - - description: Network Name + - description: HostID in: path - name: networkname + name: hostid required: true type: string - x-go-name: NetworkName - - description: ACL Container + x-go-name: HostID + responses: + "200": + $ref: '#/responses/networkBodyResponse' + schemes: + - https + summary: Update keys for a network. + tags: + - hosts + /api/hosts/adm/authenticate: + post: + operationId: authenticateHost + responses: + "200": + $ref: '#/responses/successResponse' + schemes: + - https + summary: Host based authentication for making further API calls. + tags: + - authenticate + /api/hosts/keys: + post: + operationId: updateAllKeys + responses: + "200": + $ref: '#/responses/networkBodyResponse' + schemes: + - https + summary: Update keys for a network. + tags: + - hosts + /api/networks: + get: + operationId: getNetworks + responses: + "200": + $ref: '#/responses/getNetworksSliceResponse' + schemes: + - https + summary: Lists all networks. + tags: + - networks + post: + operationId: createNetwork + parameters: + - description: Network in: body - name: acl_container + name: network schema: - $ref: '#/definitions/ACLContainer' - x-go-name: ACLContainer + $ref: '#/definitions/Network' + x-go-name: Network + responses: + "200": + $ref: '#/responses/networkBodyResponse' + schemes: + - https + summary: Create a network. + tags: + - networks + /api/networks/{networkname}: + delete: + operationId: deleteNetwork + parameters: + - description: 'name: network name' + in: path + name: networkname + required: true + type: string + x-go-name: Networkname responses: "200": - $ref: '#/responses/aclContainerResponse' + $ref: '#/responses/successResponse' schemes: - https - summary: Update a network ACL (Access Control List). + summary: Delete a network. Will not delete if there are any nodes that belong to the network. tags: - networks - /api/networks/{networkname}/keys: get: - operationId: getAccessKeys + operationId: getNetwork parameters: - - description: Network Name + - description: 'name: network name' in: path name: networkname required: true type: string - x-go-name: NetworkName + x-go-name: Networkname responses: "200": - $ref: '#/responses/accessKeySliceBodyResponse' + $ref: '#/responses/networkBodyResponse' schemes: - https - summary: Get network access keys for a network. + summary: Get a network. tags: - networks - post: - operationId: createAccessKey + put: + operationId: updateNetwork parameters: - - description: Network Name + - description: 'name: network name' in: path name: networkname required: true type: string - x-go-name: NetworkName - - description: Access Key + x-go-name: Networkname + - description: Network in: body - name: access_key + name: network schema: - $ref: '#/definitions/AccessKey' - x-go-name: AccessKey + $ref: '#/definitions/Network' + x-go-name: Network responses: "200": - $ref: '#/responses/accessKeyBodyResponse' + $ref: '#/responses/networkBodyResponse' schemes: - https - summary: Create a network access key. + summary: Update pro settings for a network. tags: - networks - /api/networks/{networkname}/keys/{name}: - delete: - operationId: deleteAccessKey + /api/networks/{networkname}/acls: + get: + operationId: getNetworkACL parameters: - - description: Network Name + - description: 'name: network name' in: path name: networkname required: true type: string - x-go-name: NetworkName - - description: Access Key Name - in: path - name: access_key_name - required: true - type: string - x-go-name: AccessKeyName + x-go-name: Networkname + - description: ACL Container + in: body + name: acl_container + schema: + $ref: '#/definitions/ACLContainer' + x-go-name: ACLContainer responses: "200": - description: "" + $ref: '#/responses/aclContainerResponse' schemes: - https - summary: Delete a network access key. + summary: Get a network ACL (Access Control List). tags: - networks - /api/networks/{networkname}/keyupdate: - post: - operationId: keyUpdate + put: + operationId: updateNetworkACL parameters: - - description: Network Name + - description: 'name: network name' in: path name: networkname required: true type: string - x-go-name: NetworkName + x-go-name: Networkname + - description: ACL Container + in: body + name: acl_container + schema: + $ref: '#/definitions/ACLContainer' + x-go-name: ACLContainer responses: "200": - $ref: '#/responses/networkBodyResponse' + $ref: '#/responses/aclContainerResponse' schemes: - https - summary: Update keys for a network. + summary: Update a network ACL (Access Control List). tags: - networks /api/nodes: @@ -1244,6 +1779,13 @@ paths: /api/nodes/{network}: get: operationId: getNetworkNodes + parameters: + - description: Network + in: path + name: network + required: true + type: string + x-go-name: Network responses: "200": $ref: '#/responses/nodeSliceResponse' @@ -1252,28 +1794,16 @@ paths: summary: Gets all nodes associated with network including pending nodes. tags: - nodes - post: - operationId: createNode - responses: - "200": - $ref: '#/responses/nodeGetResponse' - schemes: - - https - summary: Create a node on a network. - tags: - - nodes /api/nodes/{network}/{nodeid}: delete: operationId: deleteNode parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1282,7 +1812,7 @@ paths: in: body name: node schema: - $ref: '#/definitions/Node' + $ref: '#/definitions/LegacyNode' x-go-name: Node responses: "200": @@ -1295,14 +1825,12 @@ paths: get: operationId: getNode parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1318,14 +1846,12 @@ paths: put: operationId: updateNode parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1334,7 +1860,7 @@ paths: in: body name: node schema: - $ref: '#/definitions/Node' + $ref: '#/definitions/LegacyNode' x-go-name: Node responses: "200": @@ -1344,42 +1870,16 @@ paths: summary: Update an individual node. tags: - nodes - /api/nodes/{network}/{nodeid}/approve: - post: - operationId: uncordonNode - parameters: - - description: Network - in: path - name: network - required: true - type: string - x-go-name: Network - - description: Node ID - in: path - name: nodeid - required: true - type: string - x-go-name: NodeID - responses: - "200": - $ref: '#/responses/nodeResponse' - schemes: - - https - summary: Takes a node out of pending state. - tags: - - nodes /api/nodes/{network}/{nodeid}/creategateway: post: operationId: createEgressGateway parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1402,14 +1902,12 @@ paths: post: operationId: createIngressGateway parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1426,14 +1924,12 @@ paths: post: operationId: createRelay parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1456,14 +1952,12 @@ paths: delete: operationId: deleteEgressGateway parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1480,14 +1974,12 @@ paths: delete: operationId: deleteIngressGateway parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1504,14 +1996,12 @@ paths: delete: operationId: deleteRelay parameters: - - description: Network - in: path + - in: path name: network required: true type: string x-go-name: Network - - description: Node ID - in: path + - in: path name: nodeid required: true type: string @@ -1524,10 +2014,38 @@ paths: summary: Remove a relay. tags: - nodes + /api/nodes/{network}/{nodeid}/ingress/users: + get: + operationId: ingressGatewayUsers + parameters: + - in: path + name: network + required: true + type: string + x-go-name: Network + - in: path + name: nodeid + required: true + type: string + x-go-name: NodeID + responses: + "200": + $ref: '#/responses/nodeResponse' + schemes: + - https + summary: Lists all the users attached to an ingress gateway. + tags: + - users /api/nodes/adm/{network}/authenticate: post: operationId: authenticate parameters: + - description: network + in: path + name: network + required: true + type: string + x-go-name: Network - description: AuthParams in: body name: auth_params @@ -1541,10 +2059,13 @@ paths: - https summary: Authenticate to make further API calls related to a network. tags: - - nodes + - authenticate /api/oauth/login: get: operationId: HandleAuthLogin + responses: + "200": + $ref: '#/responses/okResponse' schemes: - https summary: Handles OAuth login. @@ -1572,22 +2093,15 @@ paths: summary: Get the server configuration. tags: - server - /api/server/register: - post: - operationId: register - parameters: - - description: Register Request - in: body - name: register_request - schema: - $ref: '#/definitions/RegisterRequest' - x-go-name: RegisterRequest + /api/server/status: + get: + operationId: getStatus responses: "200": - $ref: '#/responses/registerResponse' + $ref: '#/responses/serverConfigResponse' schemes: - https - summary: Registers a client with the server and return the Certificate Authority and certificate. + summary: Get the server configuration. tags: - server /api/users: @@ -1682,12 +2196,27 @@ paths: summary: Update a user. tags: - user - /api/users/{username}/adm: - put: - operationId: updateUserAdm + /api/users/{username}/remote_access_gw: + delete: + operationId: removeUserFromRemoteAccessGW parameters: - - description: Username - in: path + - in: path + name: username + required: true + type: string + x-go-name: Username + responses: + "200": + $ref: '#/responses/userBodyResponse' + schemes: + - https + summary: Delete User from a remote access gateway. + tags: + - user + post: + operationId: attachUserToRemoteAccessGateway + parameters: + - in: path name: username required: true type: string @@ -1697,7 +2226,7 @@ paths: $ref: '#/responses/userBodyResponse' schemes: - https - summary: Updates the given admin user's info (as long as the user is an admin). + summary: Attach User to a remote access gateway. tags: - user /api/users/adm/authenticate: @@ -1715,10 +2244,10 @@ paths: $ref: '#/responses/successResponse' schemes: - https - summary: Node authenticates using its password and retrieves a JWT for authorization. + summary: User authenticates using its password and retrieves a JWT for authorization. tags: - - user - /api/users/adm/createadmin: + - authenticate + /api/users/adm/createsuperadmin: post: operationId: createAdmin parameters: @@ -1736,44 +2265,134 @@ paths: summary: Make a user an admin. tags: - user - /api/users/adm/hasadmin: + /api/users/adm/hassuperadmin: get: - operationId: hasAdmin + operationId: hasSuperAdmin responses: "200": - $ref: '#/responses/successResponse' + $ref: '#/responses/hasAdmin' schemes: - https summary: Checks whether the server has an admin. tags: - user - /api/users/networks/{username}: - put: - operationId: updateUserNetworks + /api/users/adm/transfersuperadmin: + post: + operationId: transferSuperAdmin + responses: + "200": + $ref: '#/responses/userBodyResponse' + schemes: + - https + summary: Transfers superadmin role to an admin user. + tags: + - user + /api/v1/enrollment-keys: + get: + operationId: getEnrollmentKeys + responses: + "200": + $ref: '#/responses/EnrollmentKeys' + schemes: + - https + summary: Lists all EnrollmentKeys for admins. + tags: + - enrollmentKeys + post: + operationId: createEnrollmentKey + responses: + "200": + $ref: '#/responses/EnrollmentKey' + schemes: + - https + summary: Creates an EnrollmentKey for hosts to use on Netmaker server. + tags: + - enrollmentKeys + /api/v1/enrollment-keys/{keyid}: + delete: + operationId: deleteEnrollmentKey parameters: - - description: User - in: body - name: user - schema: - $ref: '#/definitions/User' - x-go-name: User - - description: Username - in: path - name: username + - in: path + name: keyid required: true type: string - x-go-name: Username + x-go-name: KeyID responses: "200": - $ref: '#/responses/userBodyResponse' + $ref: '#/responses/okResponse' schemes: - https - summary: Updates the networks of the given user. + summary: Deletes an EnrollmentKey from Netmaker server. tags: - - user + - enrollmentKeys + /api/v1/enrollment-keys/{token}: + post: + operationId: handleHostRegister + parameters: + - in: path + name: token + required: true + type: string + x-go-name: Token + - in: body + name: host + schema: + $ref: '#/definitions/Host' + x-go-name: Host + responses: + "200": + $ref: '#/responses/RegisterResponse' + schemes: + - https + summary: Handles a Netclient registration with server and add nodes accordingly. + tags: + - enrollmentKeys + /api/v1/host: + get: + description: Used by clients for "pull" command + operationId: pullHost + responses: + "200": + $ref: '#/responses/hostPull' + schemes: + - https + tags: + - hosts + /api/v1/legacy/nodes: + delete: + operationId: wipeLegacyNodes + responses: + "200": + $ref: '#/responses/successResponse' + schemes: + - https + summary: Delete all legacy nodes from DB. + tags: + - nodes + /api/v1/nodes/migrate: + put: + operationId: migrateData + responses: + "200": + $ref: '#/responses/hostPull' + schemes: + - https + summary: Used to migrate a legacy node. + tags: + - nodes /meshclient/files/{filename}: get: - operationId: fileServer + operationId: getFile + parameters: + - description: Filename + in: path + name: filename + required: true + type: string + x-go-name: Filename + responses: + "200": + $ref: '#/responses/fileResponse' schemes: - https summary: Retrieve a file from the file server. @@ -1782,22 +2401,28 @@ paths: produces: - application/json responses: - accessKeyBodyResponse: + EnrollmentKey: description: "" schema: - $ref: '#/definitions/AccessKey' - accessKeySliceBodyResponse: + $ref: '#/definitions/EnrollmentKey' + EnrollmentKeys: description: "" schema: items: - $ref: '#/definitions/AccessKey' + $ref: '#/definitions/EnrollmentKey' type: array + RegisterResponse: + description: "" + schema: + $ref: '#/definitions/RegisterResponse' aclContainerResponse: description: "" schema: $ref: '#/definitions/ACLContainer' - boolResponse: + apiHostResponse: description: "" + schema: + $ref: '#/definitions/ApiHost' byteArrayResponse: description: "" schema: @@ -1821,36 +2446,46 @@ responses: items: $ref: '#/definitions/ExtClient' type: array + fileResponse: + description: "" + schema: + $ref: '#/definitions/File' getNetworksSliceResponse: description: "" schema: items: $ref: '#/definitions/Network' type: array - networkBodyResponse: + hasAdmin: description: "" - schema: - $ref: '#/definitions/Network' - nodeGetResponse: + hostPull: description: "" schema: - $ref: '#/definitions/NodeGet' - nodeLastModifiedResponse: + $ref: '#/definitions/HostPull' + networkBodyResponse: description: "" + schema: + $ref: '#/definitions/Network' nodeResponse: description: "" schema: - $ref: '#/definitions/Node' + $ref: '#/definitions/LegacyNode' nodeSliceResponse: description: "" schema: items: - $ref: '#/definitions/Node' + $ref: '#/definitions/LegacyNode' type: array + okResponse: + description: "" serverConfigResponse: description: "" schema: $ref: '#/definitions/ServerConfig' + signal: + description: "" + schema: + $ref: '#/definitions/Signal' stringJSONResponse: description: "" successResponse: