├── .github └── dependabot.yml ├── LICENSE ├── action.yml └── README.md /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: weekly 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022 Tailscale & AUTHORS. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: "Sync Tailscale ACLs" 2 | description: "Push changes to Tailscale and run ACL tests in CI" 3 | inputs: 4 | tailnet: 5 | description: "Tailnet name (eg. example.com, xe.github, tailscale.org.github)" 6 | required: true 7 | api-key: 8 | description: "Tailscale API key" 9 | required: false 10 | oauth-client-id: 11 | description: "Tailscale OAuth ID" 12 | required: false 13 | oauth-secret: 14 | description: "Tailscale OAuth Secret" 15 | required: false 16 | policy-file: 17 | description: "Path to policy file" 18 | required: true 19 | default: ./policy.hujson 20 | action: 21 | description: "Action to take (test/apply)" 22 | required: true 23 | runs: 24 | using: "composite" 25 | steps: 26 | - name: Check Auth Info Empty 27 | if: ${{ inputs['api-key'] == '' && inputs['oauth-secret'] == '' }} 28 | shell: bash 29 | run: | 30 | echo "::error title=⛔ error hint::API Key or OAuth secret must be specified. Maybe you need to populate it in the Secrets for your workflow, see more in https://docs.github.com/en/actions/security-guides/encrypted-secrets and https://tailscale.com/s/oauth-clients" 31 | exit 1 32 | - name: Check Conflicting Auth Info 33 | if: ${{ inputs['api-key'] != '' && inputs['oauth-secret'] != '' }} 34 | shell: bash 35 | run: | 36 | echo "::error title=⛔ error hint::only one of API Key or OAuth secret should be specified. 37 | exit 1 38 | - uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0 39 | with: 40 | go-version: 1.25.5 41 | cache: false 42 | 43 | - name: Gitops pusher 44 | shell: bash 45 | env: 46 | # gitops-pusher will use OAUTH_ID and OAUTH_SECRET if non-empty, 47 | # otherwise it will use API_KEY. 48 | TS_OAUTH_ID: "${{ inputs.oauth-client-id }}" 49 | TS_OAUTH_SECRET: "${{ inputs.oauth-secret }}" 50 | TS_API_KEY: "${{ inputs.api-key }}" 51 | TS_TAILNET: "${{ inputs.tailnet }}" 52 | run: go run tailscale.com/cmd/gitops-pusher@2078eb56f3ca310821aae3fa140aa3b0d3bda2dc "--policy-file=${{ inputs.policy-file }}" "${{ inputs.action }}" 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GitHub Action to Sync Tailscale ACLs 2 | 3 | This GitHub action lets you manage your [tailnet policy file](https://tailscale.com/kb/1018/acls/) using a 4 | [GitOps](https://about.gitlab.com/topics/gitops/) workflow. With this GitHub 5 | action you can automatically manage your tailnet policy file using a git repository 6 | as your source of truth. 7 | 8 | ## Inputs 9 | 10 | ### `tailnet` 11 | 12 | **Required** The name of your tailnet. You can find it by opening [the admin 13 | panel](https://login.tailscale.com/admin) and copying down the name next to the 14 | Tailscale logo in the upper left hand corner of the page. 15 | 16 | ### `api-key` 17 | 18 | **Optional** An API key authorized for your tailnet. You can get one [in the 19 | admin panel](https://login.tailscale.com/admin/settings/keys). 20 | Either `api-key` or `oauth-client-id` and `oauth-secret` are required. 21 | 22 | Please note that API keys will expire in 90 days. Set up a monthly event to 23 | rotate your Tailscale API key, or use an OAuth client. 24 | 25 | ### `oauth-client-id` and `oauth-secret` 26 | 27 | **Optional** The ID and secret for an [OAuth client](https://tailscale.com/kb/1215/oauth-clients) 28 | for your tailnet. The client must have the `acl` scope. 29 | 30 | Either `api-key` or `oauth-client-id` and `oauth-secret` are required. 31 | 32 | ### `policy-file` 33 | 34 | **Optional** The path to your policy file in the repository. If not set this 35 | defaults to `policy.hujson` in the root of your repository. 36 | 37 | ### `action` 38 | 39 | **Required** One of `test` or `apply`. If you set `test`, the action will run 40 | ACL tests and not update the ACLs in Tailscale. If you set `apply`, the action 41 | will run ACL tests and then update the ACLs in Tailscale. This enables you to 42 | use pull requests to make changes with CI stopping you from pushing a bad change 43 | out to production. 44 | 45 | ## Getting Started 46 | 47 | Set up a new GitHub repository that will contain your tailnet policy file. Open the [Access Controls page of the admin console](https://login.tailscale.com/admin/acls) and copy your policy file to 48 | a file in that repo called `policy.hujson`. 49 | 50 | If you want to change this name to something else, you will need to add the 51 | `policy-file` argument to the `with` blocks in your GitHub Actions config. 52 | 53 | Copy this file to `.github/workflows/tailscale.yml`. 54 | 55 | ```yaml 56 | name: Sync Tailscale ACLs 57 | 58 | on: 59 | push: 60 | branches: [ "main" ] 61 | pull_request: 62 | branches: [ "main" ] 63 | 64 | jobs: 65 | acls: 66 | runs-on: ubuntu-latest 67 | 68 | steps: 69 | - uses: actions/checkout@v4 70 | 71 | - name: Fetch version-cache.json 72 | uses: actions/cache@v4 73 | with: 74 | path: ./version-cache.json 75 | key: version-cache.json-${{ github.run_id }} 76 | restore-keys: | 77 | version-cache.json- 78 | 79 | - name: Deploy ACL 80 | if: github.event_name == 'push' 81 | id: deploy-acl 82 | uses: tailscale/gitops-acl-action@v1 83 | with: 84 | api-key: ${{ secrets.TS_API_KEY }} 85 | tailnet: ${{ secrets.TS_TAILNET }} 86 | action: apply 87 | 88 | - name: Test ACL 89 | if: github.event_name == 'pull_request' 90 | id: test-acl 91 | uses: tailscale/gitops-acl-action@v1 92 | with: 93 | api-key: ${{ secrets.TS_API_KEY }} 94 | tailnet: ${{ secrets.TS_TAILNET }} 95 | action: test 96 | ``` 97 | 98 | Generate a new API key [here](https://login.tailscale.com/admin/settings/keys). 99 | 100 | Set a monthly calendar reminder to renew this key because Tailscale does not 101 | currently support API key renewal (this will be updated to support that when 102 | that feature is implemented). 103 | 104 | Then open the secrets settings for your repo and add two secrets: 105 | 106 | * `TS_API_KEY`: Your Tailscale API key from the earlier step 107 | * `TS_TAILNET`: Your tailnet's name (it's next to the logo on the upper 108 | left-hand corner of the [admin 109 | panel](https://login.tailscale.com/admin/machines)) 110 | 111 | Once you do that, commit the changes and push them to GitHub. You will have CI 112 | automatically test and push changes to your tailnet policy file to Tailscale. 113 | 114 | ## Developer guide 115 | 116 | ### Release process 117 | 118 | To create a new minor or patch release: 119 | 120 | - push the new tag to the main branch 121 | 122 | - create a new GitHub release with a description of the changes in this release 123 | 124 | - repush the latest major release tag to point at the new latest release. 125 | For example, if you are creating a `v1.3.1` release, you want to additionally tag it with `v1` tag. 126 | This approach follows the [official GitHub actions versioning guidelines](https://docs.github.com/en/actions/creating-actions/about-custom-actions#using-tags-for-release-management). 127 | --------------------------------------------------------------------------------