Locking Down GitHub Enterprise: A Security-First Approach That Actually Works
10 Sep 2025 github enterprise security identity-management devops best-practices complianceUpdated 2026-06-24: Added a Personal Access Token Security section (fine-grained PATs, lifetime enforcement, scope discipline, org-wide PAT policies, and audit log monitoring with links to the relevant GitHub audit log event categories). Also fixed a broken Custom Properties code block, moved the Access Control Flow diagram into the IAM section where it belongs, switched the audit log streaming example to env-var-style secrets, and updated GitHub Advanced Security references to the current Code Security + Secret Protection branding.
Introduction
Let’s be honest: securing GitHub Enterprise often feels like trying to nail jell-O to a wall while blindfolded. You know you need to lock things down, but you also can’t turn your development environment into a bureaucratic nightmare where opening a PR requires three forms of ID and a blood sample.
I’ve had countless conversations with enterprise customers who want to “do security right” but don’t know where to start. They’ve got compliance requirements breathing down their necks, auditors asking uncomfortable questions, and developers who will revolt if you make their workflow any more complicated than it already is.
This post is my attempt to codify the security practices that actually work in the real world – the ones I walk through with customers who are serious about protecting their intellectual property without sacrificing developer productivity. Think of this as your GitHub Enterprise security implementation guide, presentation deck, and sanity-preservation manual all rolled into one.
Fair warning: This is a living document. Security best practices evolve, new features get released, and threat landscapes shift. I’m hoping to crowdsource additional practices from the community, so if you’ve got battle-tested security configurations that aren’t covered here, let’s make this better together.
The Foundation: Enterprise Managed Users (EMU)
If you’re running GitHub Enterprise and you’re not using EMU, we need to have a serious conversation. Enterprise Managed Users is like having a security guard who actually knows everyone’s name instead of just waving people through the door.
Why EMU Matters
Traditional GitHub setups let users bring their personal accounts into your enterprise, which is like letting employees use their home keys to access the office. Sure, it’s convenient, but it’s also a security nightmare waiting to happen.
EMU creates a clean separation:
- Managed identities: All user accounts are controlled by your organization
- Centralized provisioning: Users are created and managed through your identity provider
- Clear boundaries: No mixing of personal and enterprise GitHub activities
- Audit trail: Complete visibility into who has access to what
Identity and Access Management: The “Principle of Least Privilege” Actually Applied
Set Base Permissions to None
This is where we separate the security-conscious from the “everyone gets admin because it’s easier” crowd. Setting base permissions to none means users start with zero access and earn their privileges through explicit grants.
Default configuration should be:
- Organization base permissions:
None - Repository default permissions:
None - External collaborator permissions:
None
IDP Group-Backed Teams
Manual team management is like manually managing DNS records – technically possible, but why would you torture yourself? Connect your GitHub teams directly to your identity provider groups.
Benefits:
- Automatic provisioning and deprovisioning
- Consistent access patterns across systems
- Reduced administrative overhead
- Better audit trail
Implementation checklist:
- Map GitHub teams to existing AD/LDAP groups
- Set up automated synchronization
- Define team naming conventions
- Create process for new team requests
- Establish regular access reviews
The Access Control Flow: How It All Connects
Here’s the core concept that many people struggle with – the simple flow from identity groups to repository access:
graph LR
subgraph "Your Identity Provider"
A[IDP Group:<br/>platform-engineering]
end
subgraph "GitHub"
B[GitHub Team:<br/>Platform Engineering]
C[Repository:<br/>platform-api]
D[Repository:<br/>infrastructure]
end
A -->|Syncs to| B
B -->|"Write" role| C
B -->|"Admin" role| D
style A fill:#e1f5fe
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#fff3e0
That’s it. Three simple steps:
- People are assigned to IDP groups (Active Directory, LDAP, etc.)
- IDP groups automatically map to GitHub teams (via SCIM sync)
- GitHub teams are given specific roles on specific repositories
When Sarah joins the platform engineering team:
- HR adds her to the
platform-engineeringAD group - She automatically becomes a member of the GitHub
Platform Engineeringteam - She immediately gets
Writeaccess toplatform-apiandAdminaccess toinfrastructure - No manual GitHub administration required
When she leaves or changes teams, the same process works in reverse.
Custom Organization and Repository Roles
GitHub’s default roles are like off-the-rack suits – they’ll work, but custom tailoring makes all the difference. Create roles that match your actual organizational structure and security requirements.
Example custom org roles:
- Security Reviewer: Can view security alerts and audit logs, but can’t modify settings
- Compliance Officer: Read-only access to all repositories and org settings
- Team Lead: Can manage teams and repositories within specific boundaries
Example custom repository roles:
- Code Reviewer: Can review and merge PRs, but can’t modify repository settings
- Release Manager: Can create releases and manage deployment keys
- Security Champion: Can manage security settings and view security advisories
Personal Access Token Security: The Credentials Everyone Forgets About
Personal access tokens (PATs) are the security equivalent of leaving your house key under the doormat – convenient until somebody finds it. Long-lived, over-scoped, organization-wide PATs are one of the most common attack vectors I see in enterprise breaches, and they’re almost always preventable.
If you’re letting developers create classic PATs with repo scope and no expiration, you’re effectively handing out master keys to your entire codebase. Let’s fix that.
Prefer Fine-Grained Personal Access Tokens
GitHub’s fine-grained PATs are the modern replacement for classic tokens, and they’re not even close in terms of security posture. Classic PATs grant broad scopes across every repository the user can access, while fine-grained PATs let you scope down to specific repositories with granular permissions.
Why fine-grained PATs win:
- Repository-specific access: Select exactly which repos the token can touch
- Granular permissions: Read vs. write on specific resources (contents, issues, pull requests, actions, etc.)
- Mandatory expiration: Can’t be created without an end date
- Organization approval workflow: Admins can require approval before tokens become active
- Better audit trail: Token usage is logged with specific resource access
Classic PATs should be:
- Banned outright if possible (enforce via organization settings)
- Treated as legacy credentials with an active migration plan
- Never used for new integrations or automation
Enforce Maximum Token Lifetimes
A PAT with no expiration is a credential that will outlive the employee who created it, the project it was created for, and possibly the company itself. Set organization-wide maximum lifetimes and stick to them.
Recommended lifetime tiers:
- Development/exploration: 7 days
- Standard automation: 30-90 days
- Long-running integrations: 1 year maximum (and reconsider whether a GitHub App would be better)
# Organization-level PAT policy
personal_access_tokens:
fine_grained:
enabled: true
require_approval: true
max_lifetime_days: 90
classic:
enabled: false # Disable if your workflows allow
max_lifetime_days: 30 # If you must allow them
allowed_repositories: "selected" # Not "all"
Limit Scope and Repository Access
The principle of least privilege applies double for credentials that can be exfiltrated in a single API call.
Scope discipline checklist:
- Grant only the permissions actually required (read-only when possible)
- Scope tokens to specific repositories, never “all repositories”
- Avoid
admin:org,delete_repo, and other destructive scopes unless absolutely necessary - Separate tokens by purpose – don’t reuse one token across multiple integrations
- Document what each token does and who owns it
Common over-scoping mistakes:
- Granting
repo(full control) whencontents:readwould suffice - Using
workflowscope on tokens that don’t need to modify Actions workflows - Org-wide access for tokens that only touch one or two repositories
- Write access for read-only reporting or monitoring tools
Prefer GitHub Apps and OIDC Over PATs
Honestly, the best PAT is the one you didn’t create. Before issuing a new token, ask whether the use case can be solved with something better:
- GitHub Apps: Better for service-to-service integrations, with short-lived installation tokens and granular permissions
- OIDC for CI/CD: Workload identity federation eliminates long-lived secrets entirely for cloud deployments
- GitHub Actions secrets with environment protection rules: For workflow-specific credentials
A quick word on deploy keys: they’re often suggested as a PAT alternative, but they’re really not much better. Deploy keys are shared, long-lived credentials with no individual accountability – if three services use the same deploy key and it leaks, you have no idea which one was compromised, and rotating it breaks all three at once. Treat them the same way you’d treat a classic PAT: avoid them for new integrations and migrate to GitHub Apps when you can.
PATs should be a last resort for human-driven, interactive use cases – not the default for every integration.
Enforce Organization-Wide PAT Policies
GitHub Enterprise gives you organization-level controls to enforce these practices. Use them.
Settings to lock down (Organization → Settings → Personal access tokens):
- Restrict access via fine-grained personal access tokens: Require approval for tokens accessing your org
- Allow access via fine-grained personal access tokens: Set maximum lifetime
- Restrict access via personal access tokens (classic): Disable entirely, or at minimum require justification
- Enforce SAML SSO: Require token authorization for SSO-protected orgs
Monitor and Rotate
Even with all the right policies, tokens leak. Monitor for it.
One important caveat up front: the personal_access_token and auto_approve_personal_access_token_requests audit log categories only cover fine-grained PATs – they’re tied to the organization-level approval and policy flow that classic PATs don’t participate in. Classic PATs are largely invisible in the audit log beyond org_credential_authorization events for SSO-protected orgs and indirect signals like secret scanning hits. If you want meaningful PAT telemetry, you need to be on fine-grained tokens. (Yet another reason to disable classic PATs.)
Monitoring essentials for fine-grained PATs:
- Stream PAT lifecycle events to your SIEM – at minimum
personal_access_token.request_created,personal_access_token.access_granted,personal_access_token.access_revoked, andpersonal_access_token.request_denied - Alert when policy guardrails get loosened, especially
personal_access_token.access_restriction_disabled,personal_access_token.auto_approve_grant_requests_enabled, andpersonal_access_token.expiration_limit_unset - Alert on tokens with unusual scope combinations
- Track tokens that haven’t been used in 30+ days (candidates for revocation)
- Review pending fine-grained PAT approval requests on a regular cadence
For classic PATs (until you’ve migrated everyone off):
- Watch for SSO authorization changes via
org_credential_authorization.grantandorg_credential_authorization.revoke - Use secret scanning with push protection to catch tokens accidentally committed to repositories
- Periodically inventory active classic PATs at the user level – there is no org-wide dashboard, so this is largely a manual exercise
Rotation strategy:
- Automate rotation for service-account tokens via your secret manager
- Calendar reminders for human-owned tokens approaching expiration
- Quarterly review of all active tokens in the organization
- Immediate revocation when employees change roles or leave
PAT Security Implementation Checklist
- Disable classic PATs at the organization level (or enforce short lifetimes)
- Require approval for all fine-grained PATs accessing your org
- Set maximum token lifetime to 90 days or less
- Document approved use cases for PATs vs. GitHub Apps vs. OIDC
- Migrate existing classic PATs to fine-grained tokens or GitHub Apps
- Enable secret scanning with push protection across all repositories
- Stream token-related audit events to your SIEM
- Establish a quarterly token review and cleanup process
Repository Security: Building Walls That Actually Keep Bad Things Out
Repository Rulesets: Your First Line of Defense
Rulesets are like having a bouncer who actually checks IDs at the door. They enforce your policies automatically so you don’t have to rely on developers remembering to do the right thing. Learn more about managing repositories at scale in the GitHub Well-Architected framework.
Essential ruleset configurations:
# Example branch protection ruleset
name: "Main Branch Protection"
target: "branch"
enforcement: "active"
conditions:
ref_name:
include: ["refs/heads/main", "refs/heads/master"]
rules:
- type: "required_status_checks"
parameters:
strict_required_status_checks_policy: true
required_status_checks:
- "ci/build"
- "security/scan"
- "compliance/check"
- type: "required_pull_request_reviews"
parameters:
required_approving_review_count: 2
dismiss_stale_reviews: true
require_code_owner_reviews: true
- type: "restrict_pushes"
parameters:
restrict_pushes: true
Additional ruleset best practices:
- Require signed commits for sensitive repositories
- Block force pushes to protected branches
- Require up-to-date branches before merging
- Mandate status checks for automated security scanning
Security Configurations: Defense in Depth
Security configurations let you set organization-wide defaults that actually stick. Think of them as your security policy enforcement mechanism. For comprehensive security controls, refer to the GitHub Well-Architected Application Security pillar.
Key configurations to implement:
- Dependency management:
- Enable Dependabot alerts and security updates
- Configure vulnerability scanning for all repositories
- Set up dependency review for pull requests
- Code scanning:
- Enable CodeQL analysis by default
- Configure third-party security tools integration
- Set up secret scanning across all repositories
- Supply chain security:
- Require signed commits for critical repositories
- Enable dependency graph for all repositories
- Configure SBOM generation for releases
# Example security configuration
security_and_analysis:
dependency_graph:
status: "enabled"
dependabot_alerts:
status: "enabled"
dependabot_security_updates:
status: "enabled"
secret_scanning:
status: "enabled"
secret_scanning_push_protection:
status: "enabled"
code_scanning_default_setup:
status: "enabled"
languages: ["javascript", "python", "java", "go"]
Automation Tools: Configuration as Code
Manually clicking through the GitHub UI to configure hundreds of repositories is a recipe for inconsistency and burnout. Treat your GitHub configuration like any other infrastructure – as code that’s version-controlled, reviewed, and automatically applied.
github/safe-settings
Safe-settings is an open source project that applies repository and organization settings from a central configuration repository. It’s like having an automated security guard that constantly ensures your settings match your policy.
Key benefits:
- Declarative YAML configuration stored in a
.githubrepository - Automatic remediation when settings drift from baseline
- Supports rulesets, branch protection, collaborators, and security features
- Audit trail through pull request history
Example safe-settings configuration:
# .github/repos/platform-api.yml
repository:
name: platform-api
description: Platform API service
private: true
has_issues: true
has_wiki: false
default_branch: main
allow_squash_merge: true
allow_merge_commit: false
allow_rebase_merge: false
delete_branch_on_merge: true
security_and_analysis:
secret_scanning:
status: enabled
secret_scanning_push_protection:
status: enabled
branches:
- name: main
protection:
required_pull_request_reviews:
required_approving_review_count: 2
dismiss_stale_reviews: true
require_code_owner_reviews: true
required_status_checks:
strict: true
contexts:
- "ci/build"
- "security/scan"
enforce_admins: true
restrictions: null
Terraform with the GitHub Provider
For organizations already invested in Terraform, the GitHub Terraform provider lets you manage GitHub resources alongside your other infrastructure.
When to choose Terraform over safe-settings:
- You’re already using Terraform for infrastructure management
- You need to coordinate GitHub configuration with other cloud resources
- You want to leverage Terraform’s state management and plan/apply workflow
- You need more programmatic control (loops, conditionals, modules)
Example Terraform configuration:
# Define a secure repository with all security features enabled
resource "github_repository" "platform_api" {
name = "platform-api"
description = "Platform API service"
visibility = "private"
has_issues = true
has_wiki = false
has_projects = false
allow_squash_merge = true
allow_merge_commit = false
allow_rebase_merge = false
delete_branch_on_merge = true
security_and_analysis {
secret_scanning {
status = "enabled"
}
secret_scanning_push_protection {
status = "enabled"
}
}
}
resource "github_branch_protection" "main" {
repository_id = github_repository.platform_api.node_id
pattern = "main"
required_status_checks {
strict = true
contexts = ["ci/build", "security/scan"]
}
required_pull_request_reviews {
dismiss_stale_reviews = true
required_approving_review_count = 2
require_code_owner_reviews = true
}
enforce_admins = true
}
# Apply consistent settings across multiple repositories
locals {
secure_repos = ["platform-api", "auth-service", "data-pipeline"]
}
resource "github_repository_ruleset" "security_baseline" {
for_each = toset(local.secure_repos)
name = "security-baseline"
repository = each.value
target = "branch"
enforcement = "active"
conditions {
ref_name {
include = ["~DEFAULT_BRANCH"]
exclude = []
}
}
rules {
required_signatures = true
pull_request {
required_approving_review_count = 2
dismiss_stale_reviews_on_push = true
require_code_owner_review = true
}
}
}
Automation implementation checklist:
- Choose your automation approach (safe-settings, Terraform, or both)
- Set up a dedicated configuration repository
- Define baseline security templates for different repository classifications
- Implement CI/CD pipeline for configuration changes
- Configure drift detection and alerting
- Establish review process for configuration changes
Advanced Security Configurations
Custom Properties: Metadata That Matters
Custom properties are like labels that actually do something useful. Use them to classify repositories, automate workflows, and enforce policies based on data sensitivity or compliance requirements.
Practical custom property examples:
- Data Classification:
public,internal,confidential,restricted - Compliance Scope:
sox,hipaa,pci-dss,gdpr - Business Criticality:
critical,high,medium,low - Technology Stack:
nodejs,python,java,golang
A payload for setting these values on a repository via the custom properties REST API looks like:
{
"properties": [
{ "property_name": "data_classification", "value": "confidential" },
{ "property_name": "compliance_scope", "value": ["sox", "pci-dss"] },
{ "property_name": "business_criticality", "value": "critical" },
{ "property_name": "technology_stack", "value": ["nodejs", "python"] }
]
}
Once values are set, you can target them from rulesets, security configurations, and safe-settings to apply policy based on what a repository is rather than maintaining hand-curated lists.
Inner Source Management: Controlled Sharing
Inner source is like open source, but with the training wheels still on. You want to encourage collaboration and code reuse without accidentally sharing sensitive information with the wrong people.
Inner source security checklist:
- Establish clear criteria for internal repository promotion
- Create review process for marking repositories as “internal”
- Set up automated scanning for sensitive data before visibility changes
- Define guidelines for acceptable inner source contributions
- Implement monitoring for internal repository access patterns
Process workflow:
- Developer requests to make repository internal
- Automated security scan checks for secrets, sensitive data
- Security team reviews repository content and access patterns
- Business stakeholder approves based on business value
- Repository visibility updated with appropriate custom properties
Monitoring and Compliance
Audit Log Management
If you’re not monitoring your GitHub audit logs, you’re basically flying blind through a thunderstorm. Set up comprehensive logging and alerting for security-relevant events.
Critical events to monitor:
- User provisioning and deprovisioning
- Permission changes (org, team, repository)
- Repository creation and deletion
- Security setting modifications
- Failed authentication attempts
- Unusual access patterns
Implementation strategy (illustrative pseudo-config – actual destinations are configured in the enterprise admin UI under Settings → Audit log → Log streaming; credentials should come from a secrets manager, never be hardcoded):
{
"audit_log_streaming": {
"enabled": true,
"destinations": [
{
"type": "splunk",
"endpoint": "https://splunk.company.com/services/collector",
"token": "${SPLUNK_HEC_TOKEN}"
},
{
"type": "datadog",
"endpoint": "https://http-intake.logs.datadoghq.com/api/v2/logs",
"api_key": "${DATADOG_API_KEY}"
}
],
"events": [
"repo.create",
"repo.destroy",
"org.update_member",
"team.add_member",
"team.remove_member"
]
}
}
Security Dashboards and Reporting
Create dashboards that actually tell you useful information instead of pretty graphs that nobody looks at.
Key metrics to track:
- Repository security posture scores
- Dependency vulnerability trends
- Code scanning alert resolution times
- Compliance policy violations
- User access pattern anomalies
Implementation Roadmap
Phase 1: Foundation (Weeks 1-4)
- Plan and execute EMU migration
- Configure base permissions to none
- Set up IDP group synchronization
- Create initial custom roles
Phase 2: Repository Security (Weeks 5-8)
- Implement organization-wide security configurations
- Deploy repository rulesets for critical repositories
- Set up custom properties framework
- Configure audit log streaming
Phase 3: Advanced Controls (Weeks 9-12)
- Define and implement inner source processes
- Create security monitoring dashboards
- Establish compliance reporting workflows
- Deploy advanced threat detection rules
Phase 4: Optimization (Ongoing)
- Regular access reviews and cleanup
- Security configuration tuning
- Process refinement based on usage patterns
- Continuous improvement based on new threats
Additional Security Considerations (Help Me Build This List!)
Here are some additional areas I typically discuss with customers, but I know there’s more. This is where I need your help – what am I missing?
Secret Management
- GitHub Secret Protection (secret scanning + push protection)
- Integration with enterprise secret management tools
- Custom secret scanning patterns for proprietary systems
- Automated secret remediation workflows
Network Security
- IP allow lists for organization access
- VPN integration for sensitive repositories
- Network segmentation for GitHub Enterprise Server
Backup and Disaster Recovery
- Repository backup strategies
- Metadata and configuration backup
- Disaster recovery testing procedures
- Cross-region replication considerations
Third-Party Integrations
- Security review process for GitHub Apps
- OAuth application management
- Webhook security configurations
- API token lifecycle management
What else should be on this list? Drop me a line or open an issue if you’ve got battle-tested security practices that should be included here.
Summary and Action Items
Securing GitHub Enterprise isn’t about implementing every possible control – it’s about implementing the right controls in the right order to achieve your security goals without destroying developer productivity.
Your Security Implementation Checklist
Immediate Actions (Do This Week):
- Audit your current GitHub Enterprise configuration
- Plan your EMU migration timeline
- Review and document your current permissions model
- Identify repositories that need immediate protection
Short-term Goals (Next 30 Days):
- Implement EMU or finalize migration plan
- Set base permissions to none
- Configure IDP group synchronization
- Deploy repository rulesets for critical repositories
- Set up basic security configurations
Long-term Strategy (Next 90 Days):
- Complete custom role implementation
- Establish inner source governance process
- Deploy comprehensive monitoring and alerting
- Create security reporting dashboards
- Conduct first quarterly access review
The Reality Check
Here’s the truth: security is a journey, not a destination. Your GitHub Enterprise security posture should evolve with your organization, your threat landscape, and the platform itself. The configurations I’ve outlined here are a starting point, not a finish line.
The most important thing is to start with the fundamentals (EMU, least privilege, rulesets) and build from there. Don’t try to implement everything at once – you’ll overwhelm your team and probably break something important in the process.
Community Contribution
This post is my attempt to create a repeatable, practical guide for GitHub Enterprise security discussions. But I know I’m not covering everything, and security best practices evolve faster than my ability to update blog posts.
I need your help to make this better:
- What security configurations am I missing?
- What implementation gotchas should I warn people about?
- What automation strategies have worked well in your environment?
- What compliance requirements need additional coverage?
Let’s make this the definitive guide to GitHub Enterprise security that actually helps people build more secure development environments. Because at the end of the day, security that doesn’t get implemented isn’t security at all.
Now go forth and lock things down properly – your CISO (and your developers) will thank you.
Additional Resources:
- GitHub Well-Architected Framework - Comprehensive guidance on security, governance, and operational excellence
- GitHub Enterprise Security Best Practices
- Enterprise Managed Users Documentation
- Repository Rulesets Documentation
- GitHub Code Security and Secret Protection (formerly bundled as GitHub Advanced Security)
- Audit Log Streaming Documentation
Questions about GitHub Enterprise security? Find me on LinkedIn, Bluesky, or GitHub.
Comments