Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API endpoint to get token for registering runners for repos #23761

Closed
wants to merge 2 commits into from

Conversation

techknowlogick
Copy link
Member

@techknowlogick techknowlogick commented Mar 28, 2023

This adds a new route to get a token to register new runners for specific repos

Note: This creates a pattern to create new endpoints for generating runner tokens for orgs and globally (which could be done in other PRs)

Closes #23643
Closes #24750

@techknowlogick techknowlogick added type/feature Completely new functionality. Can only be merged if feature freeze is not active. modifies/api This PR adds API routes or modifies them topic/gitea-actions related to the actions of Gitea labels Mar 28, 2023
@techknowlogick techknowlogick added this to the 1.20.0 milestone Mar 28, 2023
)

// GenerateRunnerToken generate a new runner token for a repository.
func GenerateRunnerToken(ctx *context.APIContext) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any checks to see if Actions are enabled for the instance/org/repo?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, we should probably check that.

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Mar 28, 2023
@yardenshoham
Copy link
Member

yardenshoham commented Mar 28, 2023

Closes #23643

@delvh
Copy link
Member

delvh commented Mar 28, 2023

Alternatively, what we could do as well is use one route that decides itself if owner (and repo) are set, which one to create:

  1. None provided - site wide, doer must be site admin
  2. owner provided - org wide, doer must be org admin
  3. owner+repo provided - repo wide, doer must be a repo admin

@lunny
Copy link
Member

lunny commented Mar 28, 2023

Alternatively, what we could do as well is use one route that decides itself if owner (and repo) are set, which one to create:

1. None provided - site wide, doer must be site admin

2. `owner` provided - org wide, doer must be org admin

3. `owner`+`repo` provided - repo wide, doer must be a repo admin

It's not explicit. A type field is better.

@techknowlogick
Copy link
Member Author

I could probably re-use the parseScope logic from the CLI PR

@pull-request-size pull-request-size bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label May 16, 2023
// TODO: list runners
// TODO: update runner
// TODO: delete runner
m.Put("/runners", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeActions), repo.GenerateRunnerToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that only write or even admin?

// TODO: list runners
// TODO: update runner
// TODO: delete runner
m.Put("/runners", reqToken(auth_model.AccessTokenScopeRepo), reqRepoWriter(unit.TypeActions), repo.GenerateRunnerToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, this route should probably be called /runners/new(-registration)-token or something similar.

)

// GenerateRunnerToken generate a new runner token for a repository.
func GenerateRunnerToken(ctx *context.APIContext) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, we should probably check that.

Comment on lines +37 to +38
// "404":
// "$ref": "#/responses/notFound"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about additionally returning a 404 or 403 in case actions are disabled?

@techknowlogick
Copy link
Member Author

Rather than having multiple endpoints, and combining this into one endpoint. What should the endpoint be, as /api/v1/actions doesn't exist as a parent, does it makes sense to make it just for runners?

@plomosits
Copy link

plomosits commented May 26, 2023

I think this is a very important feature, especially when using Kubernetes. I had the same problem when I wanted to deploy multiple act_runners.
Currently I've written a small workaround that uses initContainers to automatically generate new tokens per act_runner and insert them into the postgres database.

apiVersion: v1
kind: ConfigMap
metadata:
  name: gitea-runner
data:
  init.sh: |-
    #!/usr/bin/env bash
    export GITEA_RUNNER_REGISTRATION_TOKEN=$(cat /opt/gitea/token)
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gitea-runner
spec:
  replicas: 3
  selector:
    matchLabels:
      app: gitea-runner
  template:
    metadata:
      labels:
        app: gitea-runner
    spec:
      initContainers:
        - name: init-create-new-token
          image: busybox
          command:
            - sh
            - '-c'
            - tr -dc A-Za-z0-9 </dev/urandom | head -c 40 > /opt/gitea/token
          volumeMounts:
            - name: gitea-token
              mountPath: /opt/gitea
        - name: init-add-new-token
          image: governmentpaas/psql
          command:
            - sh
            - '-c'
            - >-
              GITEA_RUNNER_REGISTRATION_TOKEN=$(/bin/cat /opt/gitea/token);
              psql postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/$POSTGRES_DB -c
              "insert into public.action_runner_token
                (token, owner_id, repo_id, is_active, created, updated) values
                ('$GITEA_RUNNER_REGISTRATION_TOKEN', 0, 0, 'f', FLOOR(EXTRACT(EPOCH FROM current_timestamp)), FLOOR(EXTRACT(EPOCH FROM current_timestamp)));"
          env:
            - name: POSTGRES_HOST
              value: gitea-postgresql
            - name: POSTGRES_DB
              value: gitea
            - name: POSTGRES_USER
              value: gitea
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: gitea-postgresql
                  key: password
          volumeMounts:
            - name: gitea-token
              mountPath: /opt/gitea
      containers:
        - name: runner
          image: vegardit/gitea-act-runner:dind-latest
          env:
            - name: INIT_SH_FILE
              value: /opt/runner/init.sh
            - name: GITEA_INSTANCE_URL
              value: http://gitea-http:3000
          securityContext:
            privileged: true
          volumeMounts:
            - name: gitea-token
              mountPath: /opt/gitea
            - name: init-sh
              mountPath: /opt/runner
      volumes:
        - name: gitea-token
          emptyDir: {}
        - name: init-sh
          configMap:
            name: gitea-runner
            items:
              - key: init.sh
                path: init.sh

While this works really well, I'm not entirely happy with the solution. It would be really great if you could register a new act_runner without a token via an API. For security reasons, I think it makes sense to use admin credentials for such a call. This could easily be realized by extending the existing RegisterRequest.

message RegisterRequest {
  string name = 1;
  oneof token_or_admin_credentials {
    string token = 2;
    Credentials admin_credentials = 5;
  }
  repeated string agent_labels = 3;
  repeated string custom_labels = 4;
}

message Credentials {
  string username = 1;
  string password = 2;
}

It would also be great if there was a way to unregister act_runner as well, especially if you want them to be created and deleted dynamically, to avoid having unnecessary act_runner corpses in the database.

What do you think?

@lunny
Copy link
Member

lunny commented May 29, 2023

I could probably re-use the parseScope logic from the CLI PR

I mean we can have the type in the API interface, but in fact, stored the tokens like before

@sebthom
Copy link

sebthom commented May 30, 2023

Instead of introducing a new API, wouldn't it be more straight forward to introduce a separate scope into the regular gitea access tokens for registering runners, e.g. admin:register_runner or register:runner? What is so special about runner registration tokens?

Now you have to send a HTTP request to gitea (with a gitea token for auth) just to get a runner registration token. This feels like an unnecessary indirection.

@markopolo123
Copy link

markopolo123 commented May 30, 2023

I've just turned up looking for the ability to register and deregister runners programmatically, great to see conversation and work already happening on it!

Instead of introducing a new API, wouldn't it be more straight forward to introduce a separate scope into the regular gitea tokens for registering runners, e.g. admin:register_runner or register:runner? What is so special about runner registration tokens?

Now you have to send a HTTP request to gitea (with a gitea token for auth) just to get a runner registration token. This feels like an unnecessary indirection.

my 2c on this (please bare in mind I'm not too familiar yet with the gitea codebase):

I'd like for runner tokens to be short lived, and only capable of use for registering a runner at either repo, team, org or global level. The reason for this is that that token is exposed directly to the runner. Using a gitea token here would open potential attack vectors.

@christhomas
Copy link
Contributor

apiVersion: v1
kind: ConfigMap
metadata:
name: gitea-runner
data:
init.sh: |-
#!/usr/bin/env bash
export GITEA_RUNNER_REGISTRATION_TOKEN=$(cat /opt/gitea/token)

apiVersion: apps/v1
kind: Deployment
metadata:
name: gitea-runner
spec:
replicas: 3
selector:
matchLabels:
app: gitea-runner
template:
metadata:
labels:
app: gitea-runner
spec:
initContainers:
- name: init-create-new-token
image: busybox
command:
- sh
- '-c'
- tr -dc A-Za-z0-9 </dev/urandom | head -c 40 > /opt/gitea/token
volumeMounts:
- name: gitea-token
mountPath: /opt/gitea
- name: init-add-new-token
image: governmentpaas/psql
command:
- sh
- '-c'
- >-
GITEA_RUNNER_REGISTRATION_TOKEN=$(/bin/cat /opt/gitea/token);
psql postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/$POSTGRES_DB -c
"insert into public.action_runner_token
(token, owner_id, repo_id, is_active, created, updated) values
('$GITEA_RUNNER_REGISTRATION_TOKEN', 0, 0, 'f', FLOOR(EXTRACT(EPOCH FROM current_timestamp)), FLOOR(EXTRACT(EPOCH FROM current_timestamp)));"
env:
- name: POSTGRES_HOST
value: gitea-postgresql
- name: POSTGRES_DB
value: gitea
- name: POSTGRES_USER
value: gitea
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: gitea-postgresql
key: password
volumeMounts:
- name: gitea-token
mountPath: /opt/gitea
containers:
- name: runner
image: vegardit/gitea-act-runner:dind-latest
env:
- name: INIT_SH_FILE
value: /opt/runner/init.sh
- name: GITEA_INSTANCE_URL
value: http://gitea-http:3000
securityContext:
privileged: true
volumeMounts:
- name: gitea-token
mountPath: /opt/gitea
- name: init-sh
mountPath: /opt/runner
volumes:
- name: gitea-token
emptyDir: {}
- name: init-sh
configMap:
name: gitea-runner
items:
- key: init.sh
path: init.sh

thank you! this is exactly what I needed to fix my problem, even though it would be better if you didn't need to do this

amazing work!

@techknowlogick techknowlogick deleted the api-repo-runner branch October 30, 2023 23:27
lunny added a commit that referenced this pull request Dec 27, 2023
…user and global level (#27144)

Replace #23761

---------

Co-authored-by: Denys Konovalov <kontakt@denyskon.de>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
@go-gitea go-gitea locked as resolved and limited conversation to collaborators Jan 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. modifies/api This PR adds API routes or modifies them size/L Denotes a PR that changes 100-499 lines, ignoring generated files. topic/gitea-actions related to the actions of Gitea type/feature Completely new functionality. Can only be merged if feature freeze is not active.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automated provisioning of Actions runners Provide a programmable way to register a new action runner
10 participants